<commit_before>require('module-alias/register')

const fs = require('fs')
const path = require('path')
const glob = require('glob')
const esbuild = require('esbuild')
const sassPlugin = require('esbuild-plugin-sass')

const { makePromise } = require('@/cjs/promises')
const { composePaths } = require('@/cjs/compose-paths')
const { loadTextFile, fileOnly } = require('@/cjs/files')

const MINIFY_DISTRIBUTION = false
const PLUGIN_SOURCE_NAME = 'plugin.js'

const paths = composePaths(`
  ${__dirname}
    /src                               = SRC
      /plugins
        /**/plugin.json                = SRC_PLUGINS

      /ui
        /index.js                      = SRC_UI
        /styles
          /statements-browser/
            /all.scss                  = SRC_STATEMENTS_CSS

      /bookmarklet.js                  = SRC_BOOKMARKLET
      /main.js                         = SRC_MAIN

      /_mock-server
        /index.js                      = SERVER_SCRIPT

    /dist                              = DIST
      /plugins                         = DIST_PLUGINS
      /plugins.js                      = DIST_PLUGIN_JS
      /ui.js                           = DIST_UI
      /bookmarklet.js                  = DIST_BOOKMARKLET
      /main.js                         = DIST_MAIN
      /statement.css                   = DIST_STATEMENTS_CSS

    /extension                         = EXTENSION_FOLDER
      /manifest.json                   = EXTENSION_MANIFEST
      /obis-hsbc-uk.js                 = EXTENSION_MAIN
      /statement.css                   = EXTENSION_STATEMENTS_CSS
      /ui.css                          = EXTENSION_UI_CSS
`)

// Require plugins folder before moving on...

if (!fs.statSync(paths.DIST_PLUGINS).isDirectory()) {
  process.exit(255)
}

const DO_NOT_WRITE = true
const RUN_MOCK_SERVER = process.argv.some(arg => arg === '--mock-server')
const IS_LOCAL = (process.env.NODE_ENV || 'local') === 'local'
const SOURCE_MAPS = false // IS_LOCAL
const BUILD_REPLACEMENTS = {
  'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
  'process.env.HYDRATE': `"${process.env.HYDRATE}"`
}

console.log('IS_LOCAL', IS_LOCAL)

function main() {
  return buildWebExtension()
    .then(buildBookmarklet)
    .then(buildPlugins)
    .then(() => buildUi())
    .then(() => buildMain())
    .then(() => buildStatementsCss())
    .then(maybeRunMockServer)
}

function maybeRunMockServer() {
  if (!RUN_MOCK_SERVER) {
    return
  }

  const nodemon = require('nodemon')
  nodemon({
    script: paths.SERVER_SCRIPT,
    ext: 'js,jsx,json,scss',
    env: { HYDRATE: process.env.HYDRATE }
  })
    .on('start', () => {
      console.log('nodemon started')
    })
    .on('crash', error => {
      console.log('script crashed for some reason')
      console.error(error)
    })

  process.on('SIGINT', function () {
    console.log('Caught interrupt signal')
    nodemon.emit('quit')
    process.exit()
  })
}

//
// Builders
//

function buildBookmarklet() {
  return esbuild
    .build({
      define: BUILD_REPLACEMENTS,
      entryPoints: [paths.SRC_BOOKMARKLET],
      bundle: true,
      minify: true, // Always minify bookmarklet
      platform: 'browser',
      sourcemap: SOURCE_MAPS,
      outfile: paths.DIST_BOOKMARKLET
    })
    .then(() => loadTextFile(paths.DIST_BOOKMARKLET))
    .then(bookmarkletContent =>
      fs.writeFileSync(
        paths.DIST_BOOKMARKLET,
        'javascript:' + bookmarkletContent.toString()
      )
    )
}

function buildMain(doNotWrite) {
  const options = {
    define: BUILD_REPLACEMENTS,
    entryPoints: [paths.SRC_MAIN],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    platform: 'browser',
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(!doNotWrite && { outfile: paths.DIST_MAIN })
  }
  return esbuild.build(options)
}

function buildUi(doNotWrite) {
  const options = {
    define: BUILD_REPLACEMENTS,
    entryPoints: [paths.SRC_UI],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    platform: 'browser',
    jsxFactory: 'm',
    jsxFragment: 'm.Fragment',
    plugins: [sassPlugin()],
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(doNotWrite
      ? { outdir: paths.EXTENSION_FOLDER }
      : { outfile: paths.DIST_UI })
  }
  return esbuild.build(options)
}

function buildStatementsCss(doNotWrite) {
  const options = {
    entryPoints: [paths.SRC_STATEMENTS_CSS],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    loader: { '.scss': 'css' },
    plugins: [sassPlugin()],
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(!doNotWrite && { outfile: paths.DIST_STATEMENTS_CSS })
  }
  return esbuild.build(options)
}

function buildPlugins() {
  return buildAndMinifyPlugins().then(results => {
    const allPluginMeta = results.map(({ pluginMeta }) => pluginMeta)
    const allPluginContent = results.map(({ pluginContent, pluginMeta }) => ({
      dist: pluginMeta.dist,
      content: pluginContent
    }))

    const pluginPromises = allPluginContent.map(({ dist, content }) => {
      const [promise, resolve, reject] = makePromise()
      fs.writeFile(dist, content, err => (err ? reject(err) : resolve()))
      return promise
    })

    const pluginRegistryJs = buildPluginsRegistryContent(allPluginMeta)
    const [registryPromise, resolve, reject] = makePromise()
    fs.writeFile(paths.DIST_PLUGIN_JS, pluginRegistryJs, err =>
      err ? reject(err) : resolve()
    )

    return Promise.all(pluginPromises.concat(registryPromise))
  })
}

function buildWebExtension() {
  const decoder = new TextDecoder()
  const encoder = new TextEncoder()

  return buildMain(DO_NOT_WRITE)
    .then(getFullEsbuildContent)
    .then(mainContent => {
      esbuild
        .build({
          define: BUILD_REPLACEMENTS,
          entryPoints: [paths.SRC_UI],
          bundle: true,
          minify: MINIFY_DISTRIBUTION,
          platform: 'browser',
          jsxFactory: 'm',
          jsxFragment: 'm.Fragment',
          plugins: [sassPlugin()],
          sourcemap: SOURCE_MAPS,
          write: false,
          outdir: paths.EXTENSION_FOLDER
        })
        .then(uiBuild => {
          const uiContent = uiBuild.outputFiles.find(({ path }) =>
            path.includes('index.js')
          ).contents

          const uiCssContent = uiBuild.outputFiles.find(({ path }) =>
            path.includes('index.css')
          ).contents

          const [uiCssPromise, resolve, reject] = makePromise()
          fs.writeFile(paths.EXTENSION_UI_CSS, uiCssContent, err =>
            err ? reject(err) : resolve()
          )

          uiCssPromise
            .then(() => buildAndMinifyPlugins())
            .then(results => {
              const allPluginMeta = results.map(({ pluginMeta }) => pluginMeta)
              const allPluginContent = results.map(
                ({ pluginContent }) => pluginContent
              )
              const pluginRegistryJs = buildPluginsRegistryContent(
                allPluginMeta
              )
              const pluginRegistry = encoder.encode(pluginRegistryJs)

              return mergeTypedArrays([
                mainContent,
                uiContent,
                pluginRegistry,
                ...allPluginContent
              ])
            })
            .then(bundle => {
              const bundleJs = decoder.decode(bundle)

              return esbuild
                .transform(bundleJs, {
                  define: BUILD_REPLACEMENTS,
                  minify: MINIFY_DISTRIBUTION,
                  sourcemap: SOURCE_MAPS
                })
                .then(build => {
                  const { code } = build
                  const [promise, resolve, reject] = makePromise()
                  fs.writeFile(paths.EXTENSION_MAIN, code, err =>
                    err ? reject(err) : resolve()
                  )
                  return promise
                })
            })
        })
    })
    .then(() => {
      buildStatementsCss(DO_NOT_WRITE)
        .then(getFullEsbuildContent)
        .then(cssContent => {
          const css = decoder.decode(cssContent)
          const [promise, resolve, reject] = makePromise()
          fs.writeFile(paths.EXTENSION_STATEMENTS_CSS, css, err =>
            err ? reject(err) : resolve()
          )
          return promise
        })
    })
}

//
// Plugin building helpers
//

function buildPluginsRegistryContent(allPluginMeta) {
  const pluginInfo = allPluginMeta.map(result => {
    // eslint-disable-next-line no-unused-vars
    const { src, dist, ...restValue } = result
    return restValue
  })

  return `obis.registerPlugins(${JSON.stringify(pluginInfo, null, 2)});`
}

function buildAndMinifyPlugins() {
  return (
    metaForAllAvailablePlugins()
      // Bundle/minify plugins
      .then(allPluginMeta =>
        Promise.allSettled(
          allPluginMeta.map(pluginMeta =>
            esbuild
              .build({
                define: BUILD_REPLACEMENTS,
                entryPoints: [pluginMeta.src],
                bundle: true,
                minify: MINIFY_DISTRIBUTION,
                platform: 'browser',
                sourcemap: SOURCE_MAPS,
                write: false
              })
              .then(build => ({
                pluginMeta,
                pluginContent: getFullEsbuildContent(build)
              }))
          )
        )
      )
      .then(results =>
        results
          .filter(result => result.status === 'fulfilled')
          .map(result => result.value)
      )
  )
}

function metaForAllAvailablePlugins() {
  const loadPluginFile = fileName =>
    loadTextFile(fileName).then(src => ({ fileName, src }))

  return (
    allPluginFileNames()
      // Load all plugins
      .then(pluginsFiles =>
        Promise.allSettled(pluginsFiles.map(loadPluginFile))
      )
      // Build plugins.js output (but don't write it yet)
      .then(results =>
        results
          .filter(result => result.status === 'fulfilled')
          .map(result => result.value)
          .map(details => {
            const { fileName, src } = details
            const { name, description, urls } = JSON.parse(src)
            const { dir } = path.parse(fileName)
            const srcJs = path.join(dir, PLUGIN_SOURCE_NAME)
            const distJs = path.join(paths.DIST_PLUGINS, `${name}.js`)
            return {
              src: srcJs,
              dist: distJs,
              name,
              description,
              urls
            }
          })
      )
  )
}

function allPluginFileNames() {
  const [promise, resolve, reject] = makePromise()

  glob(paths.SRC_PLUGINS, {}, (err, files) => {
    return err
      ? reject(err)
      : Promise.all(files.map(fileOnly)).then(resolve, reject)
  })

  return promise
}

main()

//
// Helpers
//

function getFullEsbuildContent(build) {
  return mergeTypedArrays(
    build.outputFiles.map(out => out.contents),
    Uint8Array
  )
}

// https://stackoverflow.com/a/56993335/127928
const mergeTypedArrays = (arrays, type = Uint8Array) => {
  const result = new type(arrays.reduce((acc, arr) => acc + arr.byteLength, 0))
  let offset = 0
  arrays.forEach(arr => {
    result.set(arr, offset)
    offset += arr.byteLength
  })
  return result
}
<commit_msg>Curried writeTextFile()<commit_after>require('module-alias/register')

const fs = require('fs')
const path = require('path')
const glob = require('glob')
const esbuild = require('esbuild')
const sassPlugin = require('esbuild-plugin-sass')

const { makePromise } = require('@/cjs/promises')
const { composePaths } = require('@/cjs/compose-paths')
const { loadTextFile, fileOnly } = require('@/cjs/files')

const MINIFY_DISTRIBUTION = false
const PLUGIN_SOURCE_NAME = 'plugin.js'

const paths = composePaths(`
  ${__dirname}
    /src                               = SRC
      /plugins
        /**/plugin.json                = SRC_PLUGINS

      /ui
        /index.js                      = SRC_UI
        /styles
          /statements-browser/
            /all.scss                  = SRC_STATEMENTS_CSS

      /bookmarklet.js                  = SRC_BOOKMARKLET
      /main.js                         = SRC_MAIN

      /_mock-server
        /index.js                      = SERVER_SCRIPT

    /dist                              = DIST
      /plugins                         = DIST_PLUGINS
      /plugins.js                      = DIST_PLUGIN_JS
      /ui.js                           = DIST_UI
      /bookmarklet.js                  = DIST_BOOKMARKLET
      /main.js                         = DIST_MAIN
      /statement.css                   = DIST_STATEMENTS_CSS

    /extension                         = EXTENSION_FOLDER
      /manifest.json                   = EXTENSION_MANIFEST
      /obis-hsbc-uk.js                 = EXTENSION_MAIN
      /statement.css                   = EXTENSION_STATEMENTS_CSS
      /ui.css                          = EXTENSION_UI_CSS
`)

// Require plugins folder before moving on...

if (!fs.statSync(paths.DIST_PLUGINS).isDirectory()) {
  process.exit(255)
}

const DO_NOT_WRITE = true
const RUN_MOCK_SERVER = process.argv.some(arg => arg === '--mock-server')
const IS_LOCAL = (process.env.NODE_ENV || 'local') === 'local'
const SOURCE_MAPS = false // IS_LOCAL
const BUILD_REPLACEMENTS = {
  'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
  'process.env.HYDRATE': `"${process.env.HYDRATE}"`
}

console.log('IS_LOCAL', IS_LOCAL)

function main() {
  return buildWebExtension()
    .then(buildBookmarklet)
    .then(buildPlugins)
    .then(() => buildUi())
    .then(() => buildMain())
    .then(() => buildStatementsCss())
    .then(maybeRunMockServer)
}

function maybeRunMockServer() {
  if (!RUN_MOCK_SERVER) {
    return
  }

  const nodemon = require('nodemon')
  nodemon({
    script: paths.SERVER_SCRIPT,
    ext: 'js,jsx,json,scss',
    env: { HYDRATE: process.env.HYDRATE }
  })
    .on('start', () => {
      console.log('nodemon started')
    })
    .on('crash', error => {
      console.log('script crashed for some reason')
      console.error(error)
    })

  process.on('SIGINT', function () {
    console.log('Caught interrupt signal')
    nodemon.emit('quit')
    process.exit()
  })
}

//
// Builders
//

function buildBookmarklet() {
  return esbuild
    .build({
      define: BUILD_REPLACEMENTS,
      entryPoints: [paths.SRC_BOOKMARKLET],
      bundle: true,
      minify: true, // Always minify bookmarklet
      platform: 'browser',
      sourcemap: SOURCE_MAPS,
      outfile: paths.DIST_BOOKMARKLET
    })
    .then(() => loadTextFile(paths.DIST_BOOKMARKLET))
    .then(bookmarkletContent => 'javascript:' + bookmarkletContent.toString())
    .then(writeTextFile(paths.DIST_BOOKMARKLET))
}

function buildMain(doNotWrite) {
  const options = {
    define: BUILD_REPLACEMENTS,
    entryPoints: [paths.SRC_MAIN],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    platform: 'browser',
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(!doNotWrite && { outfile: paths.DIST_MAIN })
  }
  return esbuild.build(options)
}

function buildUi(doNotWrite) {
  const options = {
    define: BUILD_REPLACEMENTS,
    entryPoints: [paths.SRC_UI],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    platform: 'browser',
    jsxFactory: 'm',
    jsxFragment: 'm.Fragment',
    plugins: [sassPlugin()],
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(doNotWrite
      ? { outdir: paths.EXTENSION_FOLDER }
      : { outfile: paths.DIST_UI })
  }
  return esbuild.build(options)
}

function buildStatementsCss(doNotWrite) {
  const options = {
    entryPoints: [paths.SRC_STATEMENTS_CSS],
    bundle: true,
    minify: MINIFY_DISTRIBUTION,
    loader: { '.scss': 'css' },
    plugins: [sassPlugin()],
    sourcemap: SOURCE_MAPS,
    write: !doNotWrite,
    ...(!doNotWrite && { outfile: paths.DIST_STATEMENTS_CSS })
  }
  return esbuild.build(options)
}

function buildPlugins() {
  return buildAndMinifyPlugins().then(results => {
    const allPluginMeta = results.map(({ pluginMeta }) => pluginMeta)
    const allPluginContent = results.map(({ pluginContent, pluginMeta }) => ({
      dist: pluginMeta.dist,
      content: pluginContent
    }))
    const pluginPromises = allPluginContent.map(({ dist, content }) =>
      writeTextFile(dist)(content)
    )
    const registryPromise = writeTextFile(paths.DIST_PLUGIN_JS)(
      buildPluginsRegistry(allPluginMeta)
    )
    return Promise.all(pluginPromises.concat(registryPromise))
  })
}

function buildWebExtension() {
  const decoder = new TextDecoder()
  const encoder = new TextEncoder()

  return buildMain(DO_NOT_WRITE)
    .then(getFullEsbuildContent)
    .then(mainContent => {
      esbuild
        .build({
          define: BUILD_REPLACEMENTS,
          entryPoints: [paths.SRC_UI],
          bundle: true,
          minify: MINIFY_DISTRIBUTION,
          platform: 'browser',
          jsxFactory: 'm',
          jsxFragment: 'm.Fragment',
          plugins: [sassPlugin()],
          sourcemap: SOURCE_MAPS,
          write: false,
          outdir: paths.EXTENSION_FOLDER
        })
        .then(uiBuild => {
          const uiContent = uiBuild.outputFiles.find(({ path }) =>
            path.includes('index.js')
          ).contents

          const uiCssContent = uiBuild.outputFiles.find(({ path }) =>
            path.includes('index.css')
          ).contents

          const [uiCssPromise, resolve, reject] = makePromise()
          fs.writeFile(paths.EXTENSION_UI_CSS, uiCssContent, err =>
            err ? reject(err) : resolve()
          )

          uiCssPromise
            .then(() => buildAndMinifyPlugins())
            .then(results => {
              const allPluginMeta = results.map(({ pluginMeta }) => pluginMeta)
              const allPluginContent = results.map(
                ({ pluginContent }) => pluginContent
              )
              const pluginRegistryJs = buildPluginsRegistryContent(
                allPluginMeta
              )
              const pluginRegistry = encoder.encode(pluginRegistryJs)

              return mergeTypedArrays([
                mainContent,
                uiContent,
                pluginRegistry,
                ...allPluginContent
              ])
            })
            .then(bundle => {
              const bundleJs = decoder.decode(bundle)

              return esbuild
                .transform(bundleJs, {
                  define: BUILD_REPLACEMENTS,
                  minify: MINIFY_DISTRIBUTION,
                  sourcemap: SOURCE_MAPS
                })
                .then(build => {
                  const { code } = build
                  const [promise, resolve, reject] = makePromise()
                  fs.writeFile(paths.EXTENSION_MAIN, code, err =>
                    err ? reject(err) : resolve()
                  )
                  return promise
                })
            })
        })
    })
    .then(() => {
      buildStatementsCss(DO_NOT_WRITE)
        .then(getFullEsbuildContent)
        .then(cssContent => {
          const css = decoder.decode(cssContent)
          const [promise, resolve, reject] = makePromise()
          fs.writeFile(paths.EXTENSION_STATEMENTS_CSS, css, err =>
            err ? reject(err) : resolve()
          )
          return promise
        })
    })
}

//
// Plugin building helpers
//

function buildPluginsRegistryContent(allPluginMeta) {
  const pluginInfo = allPluginMeta.map(result => {
    // eslint-disable-next-line no-unused-vars
    const { src, dist, ...restValue } = result
    return restValue
  })

  return `obis.registerPlugins(${JSON.stringify(pluginInfo, null, 2)});`
}

function buildAndMinifyPlugins() {
  return (
    metaForAllAvailablePlugins()
      // Bundle/minify plugins
      .then(allPluginMeta =>
        Promise.allSettled(
          allPluginMeta.map(pluginMeta =>
            esbuild
              .build({
                define: BUILD_REPLACEMENTS,
                entryPoints: [pluginMeta.src],
                bundle: true,
                minify: MINIFY_DISTRIBUTION,
                platform: 'browser',
                sourcemap: SOURCE_MAPS,
                write: false
              })
              .then(build => ({
                pluginMeta,
                pluginContent: getFullEsbuildContent(build)
              }))
          )
        )
      )
      .then(results =>
        results
          .filter(result => result.status === 'fulfilled')
          .map(result => result.value)
      )
  )
}

function metaForAllAvailablePlugins() {
  const loadPluginFile = fileName =>
    loadTextFile(fileName).then(src => ({ fileName, src }))

  return (
    allPluginFileNames()
      // Load all plugins
      .then(pluginsFiles =>
        Promise.allSettled(pluginsFiles.map(loadPluginFile))
      )
      // Build plugins.js output (but don't write it yet)
      .then(results =>
        results
          .filter(result => result.status === 'fulfilled')
          .map(result => result.value)
          .map(details => {
            const { fileName, src } = details
            const { name, description, urls } = JSON.parse(src)
            const { dir } = path.parse(fileName)
            const srcJs = path.join(dir, PLUGIN_SOURCE_NAME)
            const distJs = path.join(paths.DIST_PLUGINS, `${name}.js`)
            return {
              src: srcJs,
              dist: distJs,
              name,
              description,
              urls
            }
          })
      )
  )
}

function allPluginFileNames() {
  const [promise, resolve, reject] = makePromise()

  glob(paths.SRC_PLUGINS, {}, (err, files) => {
    return err
      ? reject(err)
      : Promise.all(files.map(fileOnly)).then(resolve, reject)
  })

  return promise
}

main()

//
// Helpers
//

function writeTextFile(path) {
  return textContent => {
    const [promise, resolve, reject] = makePromise()
    fs.writeFile(path, textContent, err => (err ? reject(err) : resolve()))
    return promise
  }
}

function getFullEsbuildContent(build) {
  return mergeTypedArrays(
    build.outputFiles.map(out => out.contents),
    Uint8Array
  )
}

// https://stackoverflow.com/a/56993335/127928
const mergeTypedArrays = (arrays, type = Uint8Array) => {
  const result = new type(arrays.reduce((acc, arr) => acc + arr.byteLength, 0))
  let offset = 0
  arrays.forEach(arr => {
    result.set(arr, offset)
    offset += arr.byteLength
  })
  return result
}
<|endoftext|>


<commit_before>"use babel"
import path from "path"
import GrammarUtils from "../grammar-utils"
const { command } = GrammarUtils

function JavaArgs(filepath, context) {
  const sourcePath = GrammarUtils.Java.getProjectPath(context)
  const className = GrammarUtils.Java.getClassName(context)
  const classPackages = GrammarUtils.Java.getClassPackage(context)
  const tempFolder = GrammarUtils.createTempFolder("jar-")
  const cmd = `javac -encoding UTF-8 -sourcepath '${sourcePath}' -d '${tempFolder}' '${filepath}' && java -D'file.encoding'='UTF-8' -cp '${tempFolder}' ${classPackages}${className}`
  return GrammarUtils.formatArgs(cmd)
}

export const Java = {
  "Selection Based": {
    command,
    args(context) {
      const code = context.getCode()
      const tmpFile = GrammarUtils.createTempFileWithCode(code, ".java")
      return JavaArgs(tmpFile, context)
    },
  },
  "File Based": {
    command,
    args(context) {
      return JavaArgs(context.filepath, context)
    },
  },
}

function KotlinArgs(filepath, jar) {
  const jarNew = (jar !== null ? jar : path.basename(filepath)).replace(/\.kt$/, ".jar")
  const cmd = `kotlinc '${filepath}' -include-runtime -o ${jarNew} && java -jar ${jarNew}`
  return GrammarUtils.formatArgs(cmd)
}

export const Kotlin = {
  "Selection Based": {
    command,
    args(context) {
      const code = context.getCode()
      const tmpFile = GrammarUtils.createTempFileWithCode(code, ".kt")
      return KotlinArgs(tmpFile, null)
    },
  },
  "File Based": {
    command,
    args({ filepath, filename }) {
      return KotlinArgs(filepath, path.join(GrammarUtils.createTempFolder("kt-"), filename))
    },
  },
}

export const Processing = {
  "File Based": {
    command: "processing-java",
    args({ filepath }) {
      return [`--sketch=${path.dirname(filepath)}`, "--run"]
    },
  },
}
<commit_msg>fix: add xlint and file.encoding to Java (#2471)<commit_after>"use babel"
import path from "path"
import GrammarUtils from "../grammar-utils"
const { command } = GrammarUtils

function JavaArgs(filepath, context) {
  const sourcePath = GrammarUtils.Java.getProjectPath(context)
  const className = GrammarUtils.Java.getClassName(context)
  const classPackages = GrammarUtils.Java.getClassPackage(context)
  const tempFolder = GrammarUtils.createTempFolder("jar-")
  const cmd = `javac -encoding UTF-8 -J-Dfile.encoding=UTF-8 -Xlint -sourcepath '${sourcePath}' -d '${tempFolder}' '${filepath}' && java -Dfile.encoding=UTF-8 -cp '${tempFolder}' ${classPackages}${className}`
  return GrammarUtils.formatArgs(cmd)
}

export const Java = {
  "Selection Based": {
    command,
    args(context) {
      const code = context.getCode()
      const tmpFile = GrammarUtils.createTempFileWithCode(code, ".java")
      return JavaArgs(tmpFile, context)
    },
  },
  "File Based": {
    command,
    args(context) {
      return JavaArgs(context.filepath, context)
    },
  },
}

function KotlinArgs(filepath, jar) {
  const jarNew = (jar !== null ? jar : path.basename(filepath)).replace(/\.kt$/, ".jar")
  const cmd = `kotlinc '${filepath}' -include-runtime -o ${jarNew} && java -jar ${jarNew}`
  return GrammarUtils.formatArgs(cmd)
}

export const Kotlin = {
  "Selection Based": {
    command,
    args(context) {
      const code = context.getCode()
      const tmpFile = GrammarUtils.createTempFileWithCode(code, ".kt")
      return KotlinArgs(tmpFile, null)
    },
  },
  "File Based": {
    command,
    args({ filepath, filename }) {
      return KotlinArgs(filepath, path.join(GrammarUtils.createTempFolder("kt-"), filename))
    },
  },
}

export const Processing = {
  "File Based": {
    command: "processing-java",
    args({ filepath }) {
      return [`--sketch=${path.dirname(filepath)}`, "--run"]
    },
  },
}
<|endoftext|>


<commit_before>var metalsmith  = require('metalsmith');
var markdown    = require('metalsmith-pandoc');
var templates   = require('metalsmith-templates');
var serve       = require('metalsmith-serve');
var watch       = require('metalsmith-watch');
var moment      = require('moment');
var i18n        = require('i18next');
var headings    = require('metalsmith-headings');
var convert     = require('metalsmith-convert');

//i18n.init({ lng: "fr-FR" });
i18n.init({ lng: "en-US" });
i18n.registerAppHelper(metalsmith);

// Image handling: metalsmith-convert config

var convertConfig = {
    src: '**/*.jpg',
    target: 'jpg',
    resize: {
        width: 320,
        height: 320,
        resizeStyle: 'aspectfit'
    }
};

metalsmith(__dirname)
    .source('src')
    .use(markdown())
    .use(headings('h2'))
    .use(templates({
        engine: 'jade',
        moment: moment,
        i18n: i18n
    }))
    .use(convert(convertConfig))
    .destination('build')
    .use(serve({
        port: 8081
    }))
    .use(watch({
        paths: {
            "${source}/**/*": true,
            "templates/**/*": "**/*.md",
        }
    }))
    .build(function (err) {
        if (err) {
            throw err;
        }
    });
<commit_msg>Split out template config for readability<commit_after>var metalsmith  = require('metalsmith');
var markdown    = require('metalsmith-pandoc');
var templates   = require('metalsmith-templates');
var serve       = require('metalsmith-serve');
var watch       = require('metalsmith-watch');
var moment      = require('moment');
var i18n        = require('i18next');
var headings    = require('metalsmith-headings');
var convert     = require('metalsmith-convert');

//i18n.init({ lng: "fr-FR" });
i18n.init({ lng: "en-US" });
i18n.registerAppHelper(metalsmith);

// Image handling: metalsmith-convert config

var convertConfig = {
    src: '**/*.jpg',
    target: 'jpg',
    resize: {
        width: 320,
        height: 320,
        resizeStyle: 'aspectfit'
    }
};

// Template handling: metalsmith-templates config

var templateConfig = {
    engine: 'jade',
    moment: moment,
    i18n: i18n
}

metalsmith(__dirname)
    .source('src')
    .use(markdown())
    .use(headings('h2'))
    .use(templates(templateConfig))
    .use(convert(convertConfig))
    .destination('build')
    .use(serve({
        port: 8081
    }))
    .use(watch({
        paths: {
            "${source}/**/*": true,
            "templates/**/*": "**/*.md",
        }
    }))
    .build(function (err) {
        if (err) {
            throw err;
        }
    });
<|endoftext|>


<commit_before>parser = {};

parser.parseItalic = function(string) {

  for (var i = 0; i < string.length; i++) {
    string = string.replace(/\*([^\*]*)\*/, "::$1::");
  }

  return string;
}

parser.parseBold = function(string) {

  for (var i = 0; i < string.length; i++) {
    string = string.replace("::::", "**");
  }

  return string;
}

parser.parseMiniCode = function(string) {

  for (var i = 0; i < string.length; i++) {
  	string = string.replace("`", "%%");
  }

  return string;
}

parser.parseImages = function(string) {
  var regexp = //;

  for(var i=0; i < string.length; i++) {
    if(regexp.test(string)) {
      string = string.replace(regexp, "[img images/$1]");
    }
  }

  return string;
}

module.exports = parser;
<commit_msg>Fixing bug<commit_after>parser = {};

parser.parseItalic = function(string) {

  for (var i = 0; i < string.length; i++) {
    string = string.replace(/\*([^\*]*)\*/, "::$1::");
  }

  return string;
}

parser.parseBold = function(string) {

  for (var i = 0; i < string.length; i++) {
    string = string.replace("::::", "**");
  }

  return string;
}

parser.parseMiniCode = function(string) {

  for (var i = 0; i < string.length; i++) {
  	string = string.replace("`", "%%");
  }

  return string;
}

parser.parseImages = function(string) {
  var regexp = //;

  for(var i=0; i < string.length; i++) {
    if(regexp.test(string)) {
      string = string.replace(regexp, "[img images/$1]");
    }
  }

  return string;
}

module.exports = parser;
<|endoftext|>


<commit_before>var NwBuilder = require('node-webkit-builder');
var nw = new NwBuilder({
    files: ['gateblu-ui/**', '!build/**', '!cache/**'], // use the glob format
    platforms: ['win', 'osx', 'linux32', 'linux64'],
    appName: 'Gateblu',
    appVersion: '1.5.2',
    macIcns: './icons/gateblu.icns',
    version: '0.10.5'
});

// Log stuff you want
nw.on('log',  console.log);

// Build returns a promise
nw.build().then(function () {
   console.log('all done!');
}).catch(function (error) {
    console.error(error);
});

// And supports callbacks
nw.build(function(err) {
    if(err) console.log(err);
})

<commit_msg>update version<commit_after>var NwBuilder = require('node-webkit-builder');
var nw = new NwBuilder({
    files: ['gateblu-ui/**', '!build/**', '!cache/**'], // use the glob format
    platforms: ['win', 'osx', 'linux32', 'linux64'],
    appName: 'Gateblu',
    appVersion: '1.5.4',
    macIcns: './icons/gateblu.icns',
    version: '0.10.5'
});

// Log stuff you want
nw.on('log',  console.log);

// Build returns a promise
nw.build().then(function () {
   console.log('all done!');
}).catch(function (error) {
    console.error(error);
});

// And supports callbacks
nw.build(function(err) {
    if(err) console.log(err);
})

<|endoftext|>


<commit_before>const rollup = require('rollup').rollup;
const replace = require('rollup-plugin-replace');
const buble = require('rollup-plugin-buble');
const fs = require('fs');
const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
const zlib = require('zlib');
const UglifyJS = require('uglify-js');

const AVAIL_FEATS = [
	"FLUENT_API",
	"PARSE_TAG",
	"STATIC_CLASS",
	"AUTO_KEY",
	"SPL_ATTRS",
	"PROP_ATTRS",
	"EVENT_DELEG",
	"ONEVENT",
	"DIFF_CMP",
	"FOREIGN_ELEMS",
	"RAF_REDRAW",
	"AUTO_PX",
	"EMIT",
	"STREAM",
/*
	"HOOKS",		// deep_notify_remove, etc
    "EVENT_ATTRS",	// EVENT_PARAMS
	"REFS",
	"PREPROC_FLATTEN",
	"PREPROC_MERGE_TEXT",
	"PREPROC_REMOVE_FALSEY",
	"DIFF_CMP",	// else diff just returns true/false
	"SPREAD_BODY",
	"PARTIAL_KEYS"
	// exhaustive donor search
	// styles, hooks, attrs, events
*/
];

function getBuilds(name) {
	const pico = {
		build: "pico",
		contents: "fluent api
dom recycling
lifecycle hooks
parameterized handlers
sub-views
element injection
innerHTML
vnode refs
css objects
svg
diff
lazy list
",
		descr: "view core

**This build is unstable by design; features that get decoupled
can move to nano+ builds at any commit!**",
		feats: ["FLUENT_API"],
	};

	const nano = {
		build: "nano",
		contents: "- fluent api
+ special attrs
+ prop attrs
+ tag parsing
+ vnode patching
+ class merging
+ auto keying
+ global onevent
+ object/array diff
+ foreign elem skipping
+ raf-debounced redraw",
		descr: "`\"input[type=checkbox].some-class\"`
`vnode.patch({class: ..., style...})`",
		feats: [
			"PARSE_TAG",
			"STATIC_CLASS",
			"AUTO_KEY",
			"SPL_ATTRS",
			"PROP_ATTRS",
			"ONEVENT",
			"DIFF_CMP",
			"FOREIGN_ELEMS",
			"RAF_REDRAW",
		],
	};

	const micro = {
		build: "micro",
		contents: "+ `emit`
 + `body`
 + `autoPx`
 + `defineElementSpread`
 + `defineSvgElementSpread`
",
		descr: "`vm.emit('myNotif', arg1, arg2...)`
`vm.body()`
`{style: {width: 20}}`",
		feats: nano.feats.concat("AUTO_PX","EMIT"),
	};

	const mini = {
		build: "mini",
		contents: "+ `stream`
",
		descr: "view reactivity",
		feats: micro.feats.concat("STREAM"),
	};

	const client = {
		build: "client",
		contents: "`mini`
 + `attach`
",
		descr: "SSR hydration",
		feats: mini.feats,
	};

	const server = {
		build: "server",
		contents: "`mini`
 + `html`
",
		descr: "SSR rendering",
		feats: mini.feats,
	};

	const full = {
		build: "full",
		contents: "`mini`
 + `attach`
 + `html`
",
		descr: "all the bells and whistles",
		feats: mini.feats,
	};

	const dev = {
		build: "dev",
		contents: "`full`
 + warnings
",
		descr: "use this build for development; it contains detection of some
anti-patterns that may cause slowness, confusion, errors or
undesirable behavior",
		feats: mini.feats,
	};

	return [pico, nano, micro, mini, client, server, full, dev].filter(b => name != null ? b.build === name : true);
}

var args = process.argv.slice(2);
var buildName = args[0];
builds = buildName == null ? getBuilds().map(b => b.build) : [buildName];
builds.forEach(b => compile(b));

function getCurBranch() {
	var branches = execSync("git branch", {encoding: 'utf8'});
	return branches.match(/^\*.*$/gm)[0].substr(2);
}

function compile(buildName) {
	var start = +new Date;

	var buildFile = './src/builds/' + buildName + '.js';

	var buildCfg = getBuilds(buildName)[0];

	var feats = buildCfg.feats;

	var repls = {
		_DEVMODE: buildName === "dev",
	};

	AVAIL_FEATS.forEach(function(f) {
		repls["FEAT_" + f] = feats.indexOf(f) != -1;
	});

	rollup({
		input: buildFile,
		plugins: [
			replace(repls),
			buble(),
		],
	})
	.then(function(bundle) {
		var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
		var branch = getCurBranch();

		var ver = branch.indexOf("-dev") != -1 ? branch : "v" + pkg.version;

		var banner = [
			"/**",
			"* Copyright (c) " + new Date().getFullYear() + ", Leon Sorokin",
			"* All rights reserved. (MIT Licensed)",
			"*",
			"* domvm.js (DOM ViewModel)",
			"* A thin, fast, dependency-free vdom view layer",
			"* @preserve https://github.com/domvm/domvm (" + ver + ", " + buildName + " build)",
			"*/",
			"",
		].join("\n");

		bundle.write({
			banner: banner,
			name: "domvm",
			format: "es",		 // output format - 'amd', 'cjs', 'es', 'iife', 'umd'
		//	sourcemap: true,
			file: "./dist/" + buildName + "/domvm." + buildName + ".es.js"
		});

		bundle.write({
			banner: banner,
			name: "domvm",
			format: "umd",		 // output format - 'amd', 'cjs', 'es', 'iife', 'umd'
			sourcemap: buildName == 'full',
			file: "./dist/" + buildName + "/domvm." + buildName + ".js"
		}).then(b => {
			console.log((+new Date - start) + "ms: Rollup + Buble done (build: " + buildName + ")");
			squish(buildName, start);
		});
	}).catch(function(err) {
		console.log(err);
	})
}

function squish(buildName, start) {
	var src = "dist/" + buildName + "/domvm." + buildName + ".js";
	var dst = "dist/" + buildName + "/domvm." + buildName + ".min.js";

	// from docs (https://github.com/mishoo/UglifyJS2)
	var compressDefaults = {
		arguments: true,
		booleans: true,
		collapse_vars: true,
		comparisons: true,
		conditionals: true,
		dead_code: true,
		directives: true,
		drop_console: false,
		drop_debugger: true,
		evaluate: true,
		expression: false,
		global_defs: {},
		hoist_funs: false,
		hoist_props: true,
		hoist_vars: false,
		if_return: true,
		inline: 3,
		join_vars: true,
		keep_fargs: true,
		keep_fnames: false,
		keep_infinity: false,
		loops: true,
		negate_iife: true,
		passes: 1,
		properties: true,
		pure_funcs: null,
		pure_getters: "strict",
		reduce_funcs: true,
		reduce_vars: true,
		sequences: true,
		side_effects: true,
		switches: true,
		toplevel: false,
		top_retain: null,
		typeofs: true,
		unsafe: false,
		unsafe_comps: false,
		unsafe_Function: false,
		unsafe_math: false,
		unsafe_proto: false,
		unsafe_regexp: false,
		unsafe_undefined: false,
		unused: true,
		warnings: false,
	};

	const opts = {
		compress: Object.assign({}, compressDefaults, {
			booleans: false,
			inline: 0,
			keep_fargs: false,
			hoist_props: false,
			loops: false,
			reduce_funcs: false,
			unsafe: true,
			unsafe_math: true,
		}),
	};

	const compiled = UglifyJS.minify(fs.readFileSync(src, 'utf8'), opts).code;

	fs.writeFileSync(dst, compiled, 'utf8');

	buildDistTable();

	console.log((+new Date - start) + "ms: UglifyJS done (build: " + buildName + ")");
}

function padRight(str, padStr, len) {
	return str + padStr.repeat(len - str.length);
}

// builds markdown table
function buildDistTable() {
	var builds = getBuilds();

	var branch = getCurBranch();

	var colWidths = {
		build: 0,
		"min / gz": 0,
		contents: 0,
		descr: 0,
	};

	var appendix = [];

	builds.forEach(function(build, i) {
		var buildName = build.build;

		var path = "dist/" + buildName + "/domvm." + buildName + ".min.js";

		appendix.push("["+(i+1)+"]: https://github.com/domvm/domvm/blob/" + branch + "/" + path);

		var minified = fs.readFileSync("./" + path, 'utf8');
		var gzipped = zlib.gzipSync(minified, {level: 6});

		var minLen = (minified.length / 1024).toFixed(1);
		var gzLen = (gzipped.length / 1024).toFixed(1);

		build["min / gz"] = minLen + "k / " + gzLen + "k";
		build.build = "[" + buildName + "][" + (i+1) + "]";

		for (var colName in colWidths)
			colWidths[colName] = Math.max(colWidths[colName], build[colName].length);
	});

	var table = '';

	for (var colName in colWidths)
		table += "| " + padRight(colName, " ", colWidths[colName] + 1);
	table += "|\n";

	for (var colName in colWidths)
		table += "| " + padRight("", "-", colWidths[colName]) + " ";
	table += "|\n";

	builds.forEach(function(build, i) {
		for (var colName in colWidths)
			table += "| " + padRight(build[colName], " ", colWidths[colName] + 1);
		table += "|\n";
	});

	table += "\n" + appendix.join("\n");

	fs.writeFileSync("./dist/README.md", table, 'utf8');
}

module.exports.compile = compile;<commit_msg>const<commit_after>const rollup = require('rollup').rollup;
const replace = require('rollup-plugin-replace');
const buble = require('rollup-plugin-buble');
const fs = require('fs');
const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
const zlib = require('zlib');
const UglifyJS = require('uglify-js');

const AVAIL_FEATS = [
	"FLUENT_API",
	"PARSE_TAG",
	"STATIC_CLASS",
	"AUTO_KEY",
	"SPL_ATTRS",
	"PROP_ATTRS",
	"EVENT_DELEG",
	"ONEVENT",
	"DIFF_CMP",
	"FOREIGN_ELEMS",
	"RAF_REDRAW",
	"AUTO_PX",
	"EMIT",
	"STREAM",
/*
	"HOOKS",		// deep_notify_remove, etc
    "EVENT_ATTRS",	// EVENT_PARAMS
	"REFS",
	"PREPROC_FLATTEN",
	"PREPROC_MERGE_TEXT",
	"PREPROC_REMOVE_FALSEY",
	"DIFF_CMP",	// else diff just returns true/false
	"SPREAD_BODY",
	"PARTIAL_KEYS"
	// exhaustive donor search
	// styles, hooks, attrs, events
*/
];

function getBuilds(name) {
	const pico = {
		build: "pico",
		contents: "fluent api
dom recycling
lifecycle hooks
parameterized handlers
sub-views
element injection
innerHTML
vnode refs
css objects
svg
diff
lazy list
",
		descr: "view core

**This build is unstable by design; features that get decoupled
can move to nano+ builds at any commit!**",
		feats: ["FLUENT_API"],
	};

	const nano = {
		build: "nano",
		contents: "- fluent api
+ special attrs
+ prop attrs
+ tag parsing
+ vnode patching
+ class merging
+ auto keying
+ global onevent
+ object/array diff
+ foreign elem skipping
+ raf-debounced redraw",
		descr: "`\"input[type=checkbox].some-class\"`
`vnode.patch({class: ..., style...})`",
		feats: [
			"PARSE_TAG",
			"STATIC_CLASS",
			"AUTO_KEY",
			"SPL_ATTRS",
			"PROP_ATTRS",
			"ONEVENT",
			"DIFF_CMP",
			"FOREIGN_ELEMS",
			"RAF_REDRAW",
		],
	};

	const micro = {
		build: "micro",
		contents: "+ `emit`
 + `body`
 + `autoPx`
 + `defineElementSpread`
 + `defineSvgElementSpread`
",
		descr: "`vm.emit('myNotif', arg1, arg2...)`
`vm.body()`
`{style: {width: 20}}`",
		feats: nano.feats.concat("AUTO_PX","EMIT"),
	};

	const mini = {
		build: "mini",
		contents: "+ `stream`
",
		descr: "view reactivity",
		feats: micro.feats.concat("STREAM"),
	};

	const client = {
		build: "client",
		contents: "`mini`
 + `attach`
",
		descr: "SSR hydration",
		feats: mini.feats,
	};

	const server = {
		build: "server",
		contents: "`mini`
 + `html`
",
		descr: "SSR rendering",
		feats: mini.feats,
	};

	const full = {
		build: "full",
		contents: "`mini`
 + `attach`
 + `html`
",
		descr: "all the bells and whistles",
		feats: mini.feats,
	};

	const dev = {
		build: "dev",
		contents: "`full`
 + warnings
",
		descr: "use this build for development; it contains detection of some
anti-patterns that may cause slowness, confusion, errors or
undesirable behavior",
		feats: mini.feats,
	};

	return [pico, nano, micro, mini, client, server, full, dev].filter(b => name != null ? b.build === name : true);
}

var args = process.argv.slice(2);
var buildName = args[0];
builds = buildName == null ? getBuilds().map(b => b.build) : [buildName];
builds.forEach(b => compile(b));

function getCurBranch() {
	var branches = execSync("git branch", {encoding: 'utf8'});
	return branches.match(/^\*.*$/gm)[0].substr(2);
}

function compile(buildName) {
	var start = +new Date;

	var buildFile = './src/builds/' + buildName + '.js';

	var buildCfg = getBuilds(buildName)[0];

	var feats = buildCfg.feats;

	var repls = {
		_DEVMODE: buildName === "dev",
	};

	AVAIL_FEATS.forEach(function(f) {
		repls["FEAT_" + f] = feats.indexOf(f) != -1;
	});

	rollup({
		input: buildFile,
		plugins: [
			replace(repls),
			buble(),
		],
	})
	.then(function(bundle) {
		var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
		var branch = getCurBranch();

		var ver = branch.indexOf("-dev") != -1 ? branch : "v" + pkg.version;

		var banner = [
			"/**",
			"* Copyright (c) " + new Date().getFullYear() + ", Leon Sorokin",
			"* All rights reserved. (MIT Licensed)",
			"*",
			"* domvm.js (DOM ViewModel)",
			"* A thin, fast, dependency-free vdom view layer",
			"* @preserve https://github.com/domvm/domvm (" + ver + ", " + buildName + " build)",
			"*/",
			"",
		].join("\n");

		bundle.write({
			banner: banner,
			name: "domvm",
			format: "es",		 // output format - 'amd', 'cjs', 'es', 'iife', 'umd'
		//	sourcemap: true,
			file: "./dist/" + buildName + "/domvm." + buildName + ".es.js"
		});

		bundle.write({
			banner: banner,
			name: "domvm",
			format: "umd",		 // output format - 'amd', 'cjs', 'es', 'iife', 'umd'
			sourcemap: buildName == 'full',
			file: "./dist/" + buildName + "/domvm." + buildName + ".js"
		}).then(b => {
			console.log((+new Date - start) + "ms: Rollup + Buble done (build: " + buildName + ")");
			squish(buildName, start);
		});
	}).catch(function(err) {
		console.log(err);
	})
}

function squish(buildName, start) {
	var src = "dist/" + buildName + "/domvm." + buildName + ".js";
	var dst = "dist/" + buildName + "/domvm." + buildName + ".min.js";

	// from docs (https://github.com/mishoo/UglifyJS2)
	const compressDefaults = {
		arguments: true,
		booleans: true,
		collapse_vars: true,
		comparisons: true,
		conditionals: true,
		dead_code: true,
		directives: true,
		drop_console: false,
		drop_debugger: true,
		evaluate: true,
		expression: false,
		global_defs: {},
		hoist_funs: false,
		hoist_props: true,
		hoist_vars: false,
		if_return: true,
		inline: 3,
		join_vars: true,
		keep_fargs: true,
		keep_fnames: false,
		keep_infinity: false,
		loops: true,
		negate_iife: true,
		passes: 1,
		properties: true,
		pure_funcs: null,
		pure_getters: "strict",
		reduce_funcs: true,
		reduce_vars: true,
		sequences: true,
		side_effects: true,
		switches: true,
		toplevel: false,
		top_retain: null,
		typeofs: true,
		unsafe: false,
		unsafe_comps: false,
		unsafe_Function: false,
		unsafe_math: false,
		unsafe_proto: false,
		unsafe_regexp: false,
		unsafe_undefined: false,
		unused: true,
		warnings: false,
	};

	const opts = {
		compress: Object.assign({}, compressDefaults, {
			booleans: false,
			inline: 0,
			keep_fargs: false,
			hoist_props: false,
			loops: false,
			reduce_funcs: false,
			unsafe: true,
			unsafe_math: true,
		}),
	};

	const compiled = UglifyJS.minify(fs.readFileSync(src, 'utf8'), opts).code;

	fs.writeFileSync(dst, compiled, 'utf8');

	buildDistTable();

	console.log((+new Date - start) + "ms: UglifyJS done (build: " + buildName + ")");
}

function padRight(str, padStr, len) {
	return str + padStr.repeat(len - str.length);
}

// builds markdown table
function buildDistTable() {
	var builds = getBuilds();

	var branch = getCurBranch();

	var colWidths = {
		build: 0,
		"min / gz": 0,
		contents: 0,
		descr: 0,
	};

	var appendix = [];

	builds.forEach(function(build, i) {
		var buildName = build.build;

		var path = "dist/" + buildName + "/domvm." + buildName + ".min.js";

		appendix.push("["+(i+1)+"]: https://github.com/domvm/domvm/blob/" + branch + "/" + path);

		var minified = fs.readFileSync("./" + path, 'utf8');
		var gzipped = zlib.gzipSync(minified, {level: 6});

		var minLen = (minified.length / 1024).toFixed(1);
		var gzLen = (gzipped.length / 1024).toFixed(1);

		build["min / gz"] = minLen + "k / " + gzLen + "k";
		build.build = "[" + buildName + "][" + (i+1) + "]";

		for (var colName in colWidths)
			colWidths[colName] = Math.max(colWidths[colName], build[colName].length);
	});

	var table = '';

	for (var colName in colWidths)
		table += "| " + padRight(colName, " ", colWidths[colName] + 1);
	table += "|\n";

	for (var colName in colWidths)
		table += "| " + padRight("", "-", colWidths[colName]) + " ";
	table += "|\n";

	builds.forEach(function(build, i) {
		for (var colName in colWidths)
			table += "| " + padRight(build[colName], " ", colWidths[colName] + 1);
		table += "|\n";
	});

	table += "\n" + appendix.join("\n");

	fs.writeFileSync("./dist/README.md", table, 'utf8');
}

module.exports.compile = compile;<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var util = require('./utils.js');
var semver = require('semver');
var shell = require('shelljs');
var remote = require('selenium-webdriver/remote');

var ngdoc = require('../../node_modules/grunt-ngdocs/src/ngdoc.js');
var reader = require('../../node_modules/grunt-ngdocs/src/reader.js');

module.exports = function(grunt) {

  /*
   *
   * Create tasks
   *
   */

  grunt.registerMultiTask('tests', '**Use `grunt test` instead**', function(){
    util.startKarma.call(util, this.data, true, this.async());
  });

  // Run tests on multiple versions of angular
  grunt.registerTask('karmangular', 'Run tests against multiple versions of angular', function() {
    // Start karma servers
    var karmaOpts = grunt.config('karma');

    if (grunt.option('browsers')) {
      grunt.config('karma.options.browsers', grunt.option('browsers').split(/,/).map(function(b) { return b.trim(); }));
    }

    var angularTasks = [];
    for (var o in karmaOpts) {
      if (/^angular-/.test(o)) {
        angularTasks.push(o);
      }
    }

    // If there's a start/run argument, run that argument on each angular task
    if (this.args.length > 0) {
      // Run a test on the most recent angular
      if (this.args[0] === 'latest') {
        var latest = 'angular-' + util.latestAngular();
        var configNamespace = 'karma.' + grunt.config.escape(latest);
        grunt.config(configNamespace + '.background', false);
        grunt.config(configNamespace + '.singleRun', true);
        grunt.task.run('karma:' + latest);
      }

      if (this.args[0] === 'start') {
        angularTasks.forEach(function(t) {
          // Set this karma config to background running
          var configNamespace = 'karma.' + grunt.config.escape(t);
          grunt.config(configNamespace + '.background', true);
          grunt.config(configNamespace + '.singleRun', false);
          grunt.task.run('karma:' + t + ':start');
        });
      }
      else if (this.args[0] === 'run') {
        angularTasks.forEach(function(t) {
          // Set this karma config to background running
          var configNamespace = 'karma.' + grunt.config.escape(t);
          grunt.config(configNamespace + '.background', true);
          grunt.config(configNamespace + '.singleRun', false);
          grunt.task.run('karma:' + t + ':run');
        });
      }
    }
    else {
      angularTasks.forEach(function(t) {
        // Set this task to single running
        var configNamespace = 'karma.' + grunt.config.escape(t);
        grunt.config(configNamespace + '.background', false);
        grunt.config(configNamespace + '.singleRun', true);

        grunt.task.run('karma:' + t);
      });
    }
  });

  // Run multiple tests serially, but continue if one of them fails.
  // Adapted from http://stackoverflow.com/questions/16487681/gruntfile-getting-error-codes-from-programs-serially
  grunt.registerTask('serialsauce', function(){
    var options = grunt.config('serialsauce');

    var done = this.async();

    var tasks = {}; options.map(function(t) { tasks[t] = 0 });

    // console.log('options', this.options());

    // grunt.task.run(options);

    var success = true;
    grunt.util.async.forEachSeries(Object.keys(tasks),
      function(task, next) {
        grunt.util.spawn({
          grunt: true,  // use grunt to spawn
          args: [task], // spawn this task
          opts: { stdio: 'inherit' } // print to the same stdout
        }, function(err, result, code) {
          tasks[task] = code;
          if (code !== 0) {
            success = false;
          }
          next();
        });
      },
      function() {
        done(success);
    });
  });

  grunt.registerTask('coverage', function () {
    var latest = 'angular-' + util.latestAngular();
    var configNamespace = 'karma.' + grunt.config.escape(latest);
    grunt.config(configNamespace + '.background', false);
    grunt.config(configNamespace + '.singleRun', true);
    grunt.config(configNamespace + '.reporters', ['coverage']);
    grunt.config(configNamespace + '.preprocessors', {
      'src/**/!(*.spec)+(.js)': ['coverage']
    });
    grunt.config(configNamespace + '.coverageReporter', {
      type: 'lcov',
      dir:  'coverage',
      subdir: '.'
    });

    grunt.task.run('karma:' + latest);
  });

  grunt.registerTask('angulars', 'List available angular versions', function() {
    grunt.log.subhead("AngularJS versions available");
    grunt.log.writeln();
    util.angulars().forEach(function (a) {
      grunt.log.writeln(a);
    });
  });

  grunt.registerTask('saucebrowsers', 'List available saucelabs browsers', function() {
    grunt.log.subhead('SauceLabs Browsers Configured');
    grunt.log.writeln();

    var browsers = util.customLaunchers();
    for (var name in browsers) {
      var b = browsers[name];

      var outs = [];
      ['browserName', 'version', 'platform'].map(function (o) {
        if (b[o]) { outs.push(b[o]); }
      });

      grunt.log.write(grunt.log.wordlist([name], { color: 'yellow' }));

      grunt.log.writeln(grunt.log.wordlist([
        ' [',
        outs.join(' | '),
        ']'
      ], { color: 'green', separator: '' }));
    };
  });

  // Utility functions for showing the version
  grunt.registerTask('current-version', function () {
    grunt.log.writeln(util.getVersion());
  });
  grunt.registerTask('stable-version', function () {
    grunt.log.writeln(util.getStableVersion());
  });

  grunt.registerMultiTask('cut-release', 'Release the built code', function() {
    // Require the build and ngdocs tassk to be run first
    grunt.task.requires(['build']);

    var options = this.options({
      stableSuffix: '-stable',
      unstableSuffix: '-unstable',
      cleanup: false
    });

    var done = this.async(),
        self = this;

    var tag = util.getVersion();

    var currentTag = util.getCurrentTag();

    grunt.log.writeln("Version: " + tag);

    if (!tag) {
      grunt.fatal("Couldn't get git version");
    }

    // Figure out if the tag is stable or not (if it has a hyphen in it it's unstable)
    var stable = !/-.+$/.test(tag);
    grunt.log.writeln('stable', stable);

    // Log release type
    grunt.log.writeln(
     'Preparing '
     + grunt.log.wordlist([stable ? 'stable' : 'unstable'], { color: stable ? 'green' : 'yellow'})
     + ' release version '
     + tag
    );

    // If this is a stable release, create a directory for it in the releases dir
    var extension = stable ? options.stableSuffix : options.unstableSuffix;

    self.files.forEach(function (file) {
      var releaseDir;

      // If we're on a stable release or we want to keep unstable releases, create a directory for this release and copy the built files there
      if (currentTag || options.keepUnstable) {
        grunt.log.writeln("DEST: " + file.dest);
        grunt.log.writeln("TAG: " + tag);
        releaseDir = path.join(file.dest, tag);
      }

      file.src.forEach(function (f) {
        var oldFileName = path.basename(f);
        var ext = path.extname(f);
        var basename = path.basename(f, ext);

        if (basename.match(/.min/)) {
          ext = '.min' + ext;
          basename = path.basename(f, ext);
        }

        // Skip file if it was already released
        var re = new RegExp('(' + options.stableSuffix + '|' + options.unstableSuffix + ')');
        if (basename.match(re)) {
          grunt.log.writeln("Skipping file: " + f);
          return;
        }

        // Insert -unstable or -stable into the filename for .css and .js files
        var newFileName;
        if (/(css|js)/.test(ext)) {
          newFileName = basename + extension + ext;
        }
        else {
          newFileName = basename + ext;
        }

        // If this is a stable release
        if (releaseDir) {
          // Create the stable directory if it doesn't exist
          if (!fs.existsSync(releaseDir)) {
            fs.mkdirSync(releaseDir);
          }

          var releasePath = path.join(releaseDir, oldFileName);

          grunt.log.writeln('Copying ' + f + ' to ' + releasePath);
          // fs.createReadStream(f).pipe(fs.createWriteStream(stablePath));
          shell.cp(f, releasePath);
        }

        var newPath = path.join(file.dest, newFileName);

        grunt.log.writeln('Copying ' + f + ' to ' + newPath);
        // fs.createReadStream(f).pipe(fs.createWriteStream(newPath));
        shell.cp(f, newPath);

        if (options.cleanup) {
          grunt.log.writeln('Unlinking ' + f);
          shell.rm(f);
        }
      });
    });

    done();
  });

  // grunt.registerTask('selenium:start', function() {
  //   var done = this.async();

  //   // Get the config from the config file
  //   var configFile = path.resolve(process.cwd(), grunt.config('protractor.options.configFile'));
  //   var config = require(configFile);

  //   var server = new remote.SeleniumServer(config.seleniumServerJar, {
  //     args: config.seleniumArgs,
  //     port: config.seleniumPort
  //   });

  //   // console.log('server', server.start);

  //   server.start().then(function(url) {
  //     grunt.log.writeln('Selenium standalone server started at ' + url);

  //     done();
  //   });
  // });

  grunt.registerTask('update-bower-json', function () {
    var currentTag = semver.clean( util.getCurrentTag() );
    var jsonFile = path.resolve(process.cwd()) + '/dist/release/' + currentTag + '/bower.json';

    var json = {
      "name": "angular-ui-grid",
      "version": currentTag,
      "main": "./ui-grid.js",
      "ignore": [],
      "dependencies": {
        "angular": ">= 1.2.16"
      },
      "homepage": "http://ui-grid.info"
    };

    fs.writeFileSync(jsonFile, JSON.stringify(json, null, 2));
  });

  grunt.registerTask('blah', function () {
    reader.docs = []
    grunt.file.recurse(path.resolve(process.cwd(), 'src'), function (abspath, rootdir, subdir, filename) {
      if (!/style\.js$/.test(filename)) { return; }

      var contents = fs.readFileSync(abspath, 'utf8');

      // var d = new ngdoc.Doc(contents);
      // d.parse();
      // console.log(d);

      reader.process(contents, abspath);
    });

    console.log(reader.docs[0].convertUrlToAbsolute());
  });
};<commit_msg>fix(Release Process): Force-copy release files<commit_after>var fs = require('fs');
var path = require('path');
var util = require('./utils.js');
var semver = require('semver');
var shell = require('shelljs');
var remote = require('selenium-webdriver/remote');

var ngdoc = require('../../node_modules/grunt-ngdocs/src/ngdoc.js');
var reader = require('../../node_modules/grunt-ngdocs/src/reader.js');

module.exports = function(grunt) {

  /*
   *
   * Create tasks
   *
   */

  grunt.registerMultiTask('tests', '**Use `grunt test` instead**', function(){
    util.startKarma.call(util, this.data, true, this.async());
  });

  // Run tests on multiple versions of angular
  grunt.registerTask('karmangular', 'Run tests against multiple versions of angular', function() {
    // Start karma servers
    var karmaOpts = grunt.config('karma');

    if (grunt.option('browsers')) {
      grunt.config('karma.options.browsers', grunt.option('browsers').split(/,/).map(function(b) { return b.trim(); }));
    }

    var angularTasks = [];
    for (var o in karmaOpts) {
      if (/^angular-/.test(o)) {
        angularTasks.push(o);
      }
    }

    // If there's a start/run argument, run that argument on each angular task
    if (this.args.length > 0) {
      // Run a test on the most recent angular
      if (this.args[0] === 'latest') {
        var latest = 'angular-' + util.latestAngular();
        var configNamespace = 'karma.' + grunt.config.escape(latest);
        grunt.config(configNamespace + '.background', false);
        grunt.config(configNamespace + '.singleRun', true);
        grunt.task.run('karma:' + latest);
      }

      if (this.args[0] === 'start') {
        angularTasks.forEach(function(t) {
          // Set this karma config to background running
          var configNamespace = 'karma.' + grunt.config.escape(t);
          grunt.config(configNamespace + '.background', true);
          grunt.config(configNamespace + '.singleRun', false);
          grunt.task.run('karma:' + t + ':start');
        });
      }
      else if (this.args[0] === 'run') {
        angularTasks.forEach(function(t) {
          // Set this karma config to background running
          var configNamespace = 'karma.' + grunt.config.escape(t);
          grunt.config(configNamespace + '.background', true);
          grunt.config(configNamespace + '.singleRun', false);
          grunt.task.run('karma:' + t + ':run');
        });
      }
    }
    else {
      angularTasks.forEach(function(t) {
        // Set this task to single running
        var configNamespace = 'karma.' + grunt.config.escape(t);
        grunt.config(configNamespace + '.background', false);
        grunt.config(configNamespace + '.singleRun', true);

        grunt.task.run('karma:' + t);
      });
    }
  });

  // Run multiple tests serially, but continue if one of them fails.
  // Adapted from http://stackoverflow.com/questions/16487681/gruntfile-getting-error-codes-from-programs-serially
  grunt.registerTask('serialsauce', function(){
    var options = grunt.config('serialsauce');

    var done = this.async();

    var tasks = {}; options.map(function(t) { tasks[t] = 0 });

    // console.log('options', this.options());

    // grunt.task.run(options);

    var success = true;
    grunt.util.async.forEachSeries(Object.keys(tasks),
      function(task, next) {
        grunt.util.spawn({
          grunt: true,  // use grunt to spawn
          args: [task], // spawn this task
          opts: { stdio: 'inherit' } // print to the same stdout
        }, function(err, result, code) {
          tasks[task] = code;
          if (code !== 0) {
            success = false;
          }
          next();
        });
      },
      function() {
        done(success);
    });
  });

  grunt.registerTask('coverage', function () {
    var latest = 'angular-' + util.latestAngular();
    var configNamespace = 'karma.' + grunt.config.escape(latest);
    grunt.config(configNamespace + '.background', false);
    grunt.config(configNamespace + '.singleRun', true);
    grunt.config(configNamespace + '.reporters', ['coverage']);
    grunt.config(configNamespace + '.preprocessors', {
      'src/**/!(*.spec)+(.js)': ['coverage']
    });
    grunt.config(configNamespace + '.coverageReporter', {
      type: 'lcov',
      dir:  'coverage',
      subdir: '.'
    });

    grunt.task.run('karma:' + latest);
  });

  grunt.registerTask('angulars', 'List available angular versions', function() {
    grunt.log.subhead("AngularJS versions available");
    grunt.log.writeln();
    util.angulars().forEach(function (a) {
      grunt.log.writeln(a);
    });
  });

  grunt.registerTask('saucebrowsers', 'List available saucelabs browsers', function() {
    grunt.log.subhead('SauceLabs Browsers Configured');
    grunt.log.writeln();

    var browsers = util.customLaunchers();
    for (var name in browsers) {
      var b = browsers[name];

      var outs = [];
      ['browserName', 'version', 'platform'].map(function (o) {
        if (b[o]) { outs.push(b[o]); }
      });

      grunt.log.write(grunt.log.wordlist([name], { color: 'yellow' }));

      grunt.log.writeln(grunt.log.wordlist([
        ' [',
        outs.join(' | '),
        ']'
      ], { color: 'green', separator: '' }));
    };
  });

  // Utility functions for showing the version
  grunt.registerTask('current-version', function () {
    grunt.log.writeln(util.getVersion());
  });
  grunt.registerTask('stable-version', function () {
    grunt.log.writeln(util.getStableVersion());
  });

  grunt.registerMultiTask('cut-release', 'Release the built code', function() {
    // Require the build and ngdocs tassk to be run first
    grunt.task.requires(['build']);

    var options = this.options({
      stableSuffix: '-stable',
      unstableSuffix: '-unstable',
      cleanup: false
    });

    var done = this.async(),
        self = this;

    var tag = util.getVersion();

    var currentTag = util.getCurrentTag();

    grunt.log.writeln("Version: " + tag);

    if (!tag) {
      grunt.fatal("Couldn't get git version");
    }

    // Figure out if the tag is stable or not (if it has a hyphen in it it's unstable)
    var stable = !/-.+$/.test(tag);
    grunt.log.writeln('stable', stable);

    // Log release type
    grunt.log.writeln(
     'Preparing '
     + grunt.log.wordlist([stable ? 'stable' : 'unstable'], { color: stable ? 'green' : 'yellow'})
     + ' release version '
     + tag
    );

    // If this is a stable release, create a directory for it in the releases dir
    var extension = stable ? options.stableSuffix : options.unstableSuffix;

    self.files.forEach(function (file) {
      var releaseDir;

      // If we're on a stable release or we want to keep unstable releases, create a directory for this release and copy the built files there
      if (currentTag || options.keepUnstable) {
        grunt.log.writeln("DEST: " + file.dest);
        grunt.log.writeln("TAG: " + tag);
        releaseDir = path.join(file.dest, tag);
      }

      file.src.forEach(function (f) {
        var oldFileName = path.basename(f);
        var ext = path.extname(f);
        var basename = path.basename(f, ext);

        if (basename.match(/.min/)) {
          ext = '.min' + ext;
          basename = path.basename(f, ext);
        }

        // Skip file if it was already released
        var re = new RegExp('(' + options.stableSuffix + '|' + options.unstableSuffix + ')');
        if (basename.match(re)) {
          grunt.log.writeln("Skipping file: " + f);
          return;
        }

        // Insert -unstable or -stable into the filename for .css and .js files
        var newFileName;
        if (/(css|js)/.test(ext)) {
          newFileName = basename + extension + ext;
        }
        else {
          newFileName = basename + ext;
        }

        // If this is a stable release
        if (releaseDir) {
          // Create the stable directory if it doesn't exist
          if (!fs.existsSync(releaseDir)) {
            fs.mkdirSync(releaseDir);
          }

          var releasePath = path.join(releaseDir, oldFileName);

          grunt.log.writeln('Copying ' + f + ' to ' + releasePath);
          // fs.createReadStream(f).pipe(fs.createWriteStream(stablePath));
          shell.cp('-f', f, releasePath);
        }

        var newPath = path.join(file.dest, newFileName);

        grunt.log.writeln('Copying ' + f + ' to ' + newPath);
        // fs.createReadStream(f).pipe(fs.createWriteStream(newPath));
        shell.cp('-f', f, newPath);

        if (options.cleanup) {
          grunt.log.writeln('Unlinking ' + f);
          shell.rm(f);
        }
      });
    });

    done();
  });

  // grunt.registerTask('selenium:start', function() {
  //   var done = this.async();

  //   // Get the config from the config file
  //   var configFile = path.resolve(process.cwd(), grunt.config('protractor.options.configFile'));
  //   var config = require(configFile);

  //   var server = new remote.SeleniumServer(config.seleniumServerJar, {
  //     args: config.seleniumArgs,
  //     port: config.seleniumPort
  //   });

  //   // console.log('server', server.start);

  //   server.start().then(function(url) {
  //     grunt.log.writeln('Selenium standalone server started at ' + url);

  //     done();
  //   });
  // });

  grunt.registerTask('update-bower-json', function () {
    var currentTag = semver.clean( util.getCurrentTag() );
    var jsonFile = path.resolve(process.cwd()) + '/dist/release/' + currentTag + '/bower.json';

    var json = {
      "name": "angular-ui-grid",
      "version": currentTag,
      "main": "./ui-grid.js",
      "ignore": [],
      "dependencies": {
        "angular": ">= 1.2.16"
      },
      "homepage": "http://ui-grid.info"
    };

    fs.writeFileSync(jsonFile, JSON.stringify(json, null, 2));
  });

  grunt.registerTask('blah', function () {
    reader.docs = []
    grunt.file.recurse(path.resolve(process.cwd(), 'src'), function (abspath, rootdir, subdir, filename) {
      if (!/style\.js$/.test(filename)) { return; }

      var contents = fs.readFileSync(abspath, 'utf8');

      // var d = new ngdoc.Doc(contents);
      // d.parse();
      // console.log(d);

      reader.process(contents, abspath);
    });

    console.log(reader.docs[0].convertUrlToAbsolute());
  });
};<|endoftext|>


<commit_before>const fs = require("fs")
	path = require("path"),
	minimist = require("minimist"),
	chalk = require("chalk"),
	babel = require("@babel/core"),
	prettier = require("prettier");

// save cwd
const cwd = process.cwd();

// parse args
const argv = minimist(process.argv);

// check if plugins passed
if (!argv.plugin && !argv.plugins) {
	console.log(chalk.red("Error: No Plugins passed in the \"--plugin\" and \"--plugins\" flags."));
	return;
}

// save data
const data = {
	plugins: (argv.plugins || argv.plugin).split(","),
	version: argv.version || "v1",
	dev: argv.develop || argv.dev || false,
	devBuild: argv.devBuild || argv.devbuild || argv["dev-build"] || false
};

// check if all plugins
if (data.plugins[0] === "*") {
	data.plugins = [];
	const dir = path.resolve(cwd, "src", data.version);
	const folders = fs.readdirSync(dir, {withFileTypes: true}).filter((e) => e.isDirectory());
	for (const folder of folders) {
		data.plugins.push(folder.name);
	}
}

// check if development mode
if (data.dev) {
	if (data.plugins.length > 1) {
		console.log(chalk.red("Error: More than 1 Plugin passed in Dev mode."));
	}
	else {
		dev(Object.assign({name: data.plugins[0], watch: true}, data));
	}
}
else if (data.devBuild) {
	for (const plugin of data.plugins) {
		dev(Object.assign({name: plugin, watch: false}, data));
	}
}
else {
	for (const plugin of data.plugins) {
		build(Object.assign({name: plugin}, data));
	}
}

async function build(data) {
	try {
		const time = process.hrtime();

		// resolve source directory
		const dir = path.resolve(cwd, "src", data.version, data.name);

		// load plugin config
		const info = require(path.resolve(dir, "config.json"));

		// generate source link
		info.source = "https://github.com/Zerthox/BetterDiscord-Plugins";

		// find main file
		const main = path.resolve(dir, "main.jsx");

		// transform with babel & custom sass-importer plugin
		const transformed = babel.transformFileSync(
			main,
			{
				plugins: [
					["./lib/sass-importer", {
						plugin: info
					}]
				],
				comments: false
			}
		).code;

		// prepend meta & base
		const result = generateMeta(info) + "\n\n" + wrapWScript(generatePlugin(info, transformed));

		// load prettier config
		const cfg = await prettier.resolveConfig(path.resolve(dir));

		// format code
		const formatted = prettier.format(result, cfg);

		// determine output file
		const out = path.resolve(cwd, data.version, `${data.name}.plugin.js`);

		// save result
		fs.writeFileSync(out, formatted);

		// console output
		console.log(chalk.green(`Successfully compiled ${info.name}${info.version ? ` v${info.version}` : ""} to "${out}" [${Math.round(process.hrtime(time)[1] / 1000000)}ms]`));
	}
	catch (err) {
		console.log(chalk.red(`${err.name ? err.name : "Error"} during build:`), err.message);
	}
}

function generateMeta(info) {
	let meta = "/**";
	for (const [key, val] of Object.entries(info)) {
		meta += `\n * @${key} ${val.replace(/\n/g, "\\n")}`;
	}
	return meta + "\n */";
}

function generatePlugin(info, contents) {
	let plugin = fs.readFileSync("./lib/template/base.js", "utf8");
	for (const [key, val] of Object.entries(info)) {
		plugin = plugin.split(`@meta{${key}}`).join(val.replace(/\n/g, "\\n"));
	}
	plugin = plugin.replace(/^"contents";$/gm, contents);
	return babel.transformSync(plugin, {comments: false}).code;
}

function wrapWScript(contents) {
	const wscript = fs.readFileSync("./lib/template/wscript.js", "utf8");
	return wscript.replace(/^"contents";$/gm, contents);
}

function dev(data) {

	// resolve source directory
	const dir = path.resolve(cwd, "src", data.version, data.name);

	// find main file & config
	const file = path.resolve(dir, "main.jsx"),
		cfg = path.resolve(dir, "config.json");

	// resolve betterdiscord path
	const bdPath = path.resolve(
		process.platform === "win32" ? process.env.APPDATA : process.platform === "darwin" ? path.resolve(process.env.HOME, "Library/Preferences") : path.resolve(process.env.HOME, ".config"),
		"BetterDiscord"
	);

	// find output file path
	const out = path.resolve(bdPath, "plugins", `${data.name}.plugin.js`);

	// declare compile function
	function compile() {
		try {
			const time = process.hrtime();

			// load plugin config
			const info = require(cfg);

			// generate source link
			info.source = "https://github.com/Zerthox/BetterDiscord-Plugins";

			// transform file
			const transformed = babel.transformFileSync(
				file,
				{
					plugins: [
						["./lib/sass-importer", {
							plugin: info
						}]
					]
				}
			).code;

			// write to output file
			fs.writeFileSync(out, generateMeta(info) + generatePlugin(info, transformed));

			// console output
			console.log(chalk.green(`Compiled "${data.name}.plugin.js" to the BetterDiscord plugins folder [${Math.round(process.hrtime(time)[1] / 1000000)}ms]`));
		}
		catch (err) {

			// log error
			console.log(chalk.red(`${err.name ? err.name : "Error"} during compile:`), err.message);
		}
	};

	// compile once
	compile();

	// watch for changes if necessary
	if (data.watch) {
		console.log(`Watching for changes in "${file}"`);
		fs.watch(dir, {}, (event, filename) => {
			console.log(`=> ${event === "rename" ? "Renamed" : "Changed"} "${filename}"`);
			compile();
		});
	}
}<commit_msg>Update build script<commit_after>const pkg = require("./package.json"),
	fs = require("fs")
	path = require("path"),
	minimist = require("minimist"),
	chalk = require("chalk"),
	babel = require("@babel/core"),
	prettier = require("prettier");

// save cwd
const cwd = process.cwd();

// parse args
const argv = minimist(process.argv);

// check if plugins passed
if (!argv.plugin && !argv.plugins) {
	console.log(chalk.red("Error: No Plugins passed in the \"--plugin\" and \"--plugins\" flags."));
	return;
}

// save data
const data = {
	plugins: (argv.plugins || argv.plugin).split(","),
	version: argv.version || "v1",
	dev: argv.develop || argv.dev || false,
	devBuild: argv.devBuild || argv.devbuild || argv["dev-build"] || false
};

// check if all plugins
if (data.plugins[0] === "*") {
	data.plugins = [];
	const dir = path.resolve(cwd, "src", data.version);
	const folders = fs.readdirSync(dir, {withFileTypes: true}).filter((e) => e.isDirectory());
	for (const folder of folders) {
		data.plugins.push(folder.name);
	}
}

// check if development mode
if (data.dev) {
	if (data.plugins.length > 1) {
		console.log(chalk.red("Error: More than 1 Plugin passed in Dev mode."));
	}
	else {
		dev(Object.assign({name: data.plugins[0], watch: true}, data));
	}
}
else {
	for (const plugin of data.plugins) {
		build(Object.assign({name: plugin}, data));
	}
	if (data.devBuild) {
		for (const plugin of data.plugins) {
			dev(Object.assign({name: plugin, watch: false}, data));
		}
	}
}

async function build(data) {
	try {
		const time = process.hrtime();

		// resolve source directory
		const dir = path.resolve(cwd, "src", data.version, data.name);

		// load plugin config
		const info = require(path.resolve(dir, "config.json"));

		// generate source link
		info.source = "https://github.com/Zerthox/BetterDiscord-Plugins";

		// find main file
		const main = path.resolve(dir, "main.jsx");

		// transform with babel & custom sass-importer plugin
		const transformed = babel.transformFileSync(
			main,
			{
				plugins: [
					["./lib/sass-importer", {
						plugin: info
					}]
				],
				comments: false
			}
		).code;

		// prepend meta & base
		const result = generateMeta(info) + "\n\n" + wrapWScript(generatePlugin(info, transformed));

		// load prettier config
		const cfg = await prettier.resolveConfig(path.resolve(dir));

		// format code
		const formatted = prettier.format(result, cfg);

		// determine output file
		const out = path.resolve(cwd, data.version, `${data.name}.plugin.js`);

		// save result
		fs.writeFileSync(out, formatted);

		// console output
		console.log(chalk.green(`Successfully compiled ${info.name}${info.version ? ` v${info.version}` : ""} to "${out}" [${Math.round(process.hrtime(time)[1] / 1000000)}ms]`));
	}
	catch (err) {
		console.log(chalk.red(`${err.name ? err.name : "Error"} during build:`), err.message);
	}
}

function generateMeta(info) {
	let meta = "/**";
	for (const [key, val] of Object.entries(info)) {
		meta += `\n * @${key} ${val.replace(/\n/g, "\\n")}`;
	}
	return meta + "\n */";
}

function generatePlugin(info, contents) {
	let plugin = fs.readFileSync("./lib/template/base.js", "utf8");
	for (const [key, val] of Object.entries(info)) {
		plugin = plugin.split(`@meta{${key}}`).join(val.replace(/\n/g, "\\n"));
	}
	plugin = plugin.replace(/^"contents";$/gm, contents);
	return babel.transformSync(plugin, Object.assign({comments: false}, pkg.babel)).code;
}

function wrapWScript(contents) {
	const wscript = fs.readFileSync("./lib/template/wscript.js", "utf8");
	return wscript.replace(/^"contents";$/gm, contents);
}

function dev(data) {

	// resolve source directory
	const dir = path.resolve(cwd, "src", data.version, data.name);

	// find main file & config
	const file = path.resolve(dir, "main.jsx"),
		cfg = path.resolve(dir, "config.json");

	// resolve betterdiscord path
	const bdPath = path.resolve(
		process.platform === "win32" ? process.env.APPDATA : process.platform === "darwin" ? path.resolve(process.env.HOME, "Library/Preferences") : path.resolve(process.env.HOME, ".config"),
		"BetterDiscord"
	);

	// find output file path
	const out = path.resolve(bdPath, "plugins", `${data.name}.plugin.js`);

	// declare compile function
	function compile() {
		try {
			const time = process.hrtime();

			// load plugin config
			const info = require(cfg);

			// generate source link
			info.source = "https://github.com/Zerthox/BetterDiscord-Plugins";

			// transform file
			const transformed = babel.transformFileSync(
				file,
				{
					plugins: [
						["./lib/sass-importer", {
							plugin: info
						}]
					]
				}
			).code;

			// write to output file
			fs.writeFileSync(out, generateMeta(info) + generatePlugin(info, transformed));

			// console output
			console.log(chalk.green(`Compiled "${data.name}.plugin.js" to the BetterDiscord plugins folder [${Math.round(process.hrtime(time)[1] / 1000000)}ms]`));
		}
		catch (err) {

			// log error
			console.log(chalk.red(`${err.name ? err.name : "Error"} during compile:`), err.message);
		}
	};

	// compile once
	compile();

	// watch for changes if necessary
	if (data.watch) {
		console.log(`Watching for changes in "${file}"`);
		fs.watch(dir, {}, (event, filename) => {
			console.log(`=> ${event === "rename" ? "Renamed" : "Changed"} "${filename}"`);
			compile();
		});
	}
}<|endoftext|>


<commit_before>define('parsley/field', [
    'parsley/factory/constraint',
    'parsley/ui',
    'parsley/utils'
], function (ConstraintFactory, ParsleyUI, ParsleyUtils) {
  var ParsleyField = function (field, options, parsleyFormInstance) {
    this.__class__ = 'ParsleyField';
    this.__id__ = ParsleyUtils.hash(4);

    this.$element = $(field);

    // Set parent if we have one
    if ('undefined' !== typeof parsleyFormInstance) {
      this.parent = parsleyFormInstance;
    }

    this._resetOptions(options);

    // Initialize some properties
    this.constraints = [];
    this.constraintsByName = {};
    this.validationResult = [];

    // Bind constraints
    this._bindConstraints();
  };

  ParsleyField.prototype = {
    // # Public API
    // Validate field and trigger some events for mainly `ParsleyUI`
    // @returns validationResult:
    //  - `true` if field valid
    //  - `[Violation, [Violation...]]` if there were validation errors
    validate: function (force) {
      this.value = this.getValue();

      // Field Validate event. `this.value` could be altered for custom needs
      this._trigger('validate');

      this._trigger(this.isValid(force, this.value) ? 'success' : 'error');

      // Field validated event. `this.validationResult` could be altered for custom needs too
      this._trigger('validated');

      return this.validationResult;
    },

    hasConstraints: function () {
      return 0 !== this.constraints.length;
    },

    // An empty optional field does not need validation
    needsValidation: function (value) {
      if ('undefined' === typeof value)
        value = this.getValue();

      // If a field is empty and not required, it is valid
      // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators
      if (!value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty)
        return false;

      return true;
    },

    // Just validate field. Do not trigger any event
    //  - `false` if there are constraints and at least one of them failed
    //  - `true` in all other cases
    isValid: function (force, value) {
      // Recompute options and rebind constraints to have latest changes
      this.refreshConstraints();
      this.validationResult = true;

      // A field without constraint is valid
      if (!this.hasConstraints())
        return true;

      // Value could be passed as argument, needed to add more power to 'parsley:field:validate'
      if ('undefined' === typeof value || null === value)
        value = this.getValue();

      // An empty optional field is valid
      if (!value.length && !this.needsValidation(value) && true !== force)
        return true;

      // If we want to validate field against all constraints, just call Validator and let it do the job
      if (false === this.options.priorityEnabled)
        return true === (this.validationResult = this.validateThroughValidator(value, this.constraints, 'Any'));

      // Sort priorities to validate more important first
      var priorities = this._getConstraintsSortedPriorities();

      // Else, iterate over priorities one by one, and validate related asserts one by one
      for (var i = 0; i < priorities.length; i++)
        if (true !== (this.validationResult = this.validateThroughValidator(value, this.constraints, priorities[i])))
          return false;

      return true;
    },

    // @returns Parsley field computed value that could be overrided or configured in DOM
    getValue: function () {
      var value;

      // Value could be overriden in DOM or with explicit options
      if ('function' === typeof this.options.value)
        value = this.options.value(this);
      else if ('undefined' !== typeof this.options.value)
        value = this.options.value;
      else
        value = this.$element.val();

      // Handle wrong DOM or configurations
      if ('undefined' === typeof value || null === value)
        return '';

      // Use `data-parsley-trim-value="true"` to auto trim inputs entry
      if (true === this.options.trimValue)
        return value.replace(/^\s+|\s+$/g, '');

      return value;
    },

    // Actualize options that could have change since previous validation
    // Re-bind accordingly constraints (could be some new, removed or updated)
    refreshConstraints: function () {
      return this.actualizeOptions()._bindConstraints();
    },

    /**
    * Add a new constraint to a field
    *
    * @method addConstraint
    * @param {String}   name
    * @param {Mixed}    requirements      optional
    * @param {Number}   priority          optional
    * @param {Boolean}  isDomConstraint   optional
    */
    addConstraint: function (name, requirements, priority, isDomConstraint) {
      name = name.toLowerCase();

      if ('function' === typeof window.ParsleyValidator.validators[name]) {
        var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint);

        // if constraint already exist, delete it and push new version
        if ('undefined' !== this.constraintsByName[constraint.name])
          this.removeConstraint(constraint.name);

        this.constraints.push(constraint);
        this.constraintsByName[constraint.name] = constraint;
      }

      return this;
    },

    // Remove a constraint
    removeConstraint: function (name) {
      for (var i = 0; i < this.constraints.length; i++)
        if (name === this.constraints[i].name) {
          this.constraints.splice(i, 1);
          break;
        }
      delete this.constraintsByName[name];
      return this;
    },

    // Update a constraint (Remove + re-add)
    updateConstraint: function (name, parameters, priority) {
      return this.removeConstraint(name)
        .addConstraint(name, parameters, priority);
    },

    // # Internals

    // Internal only.
    // Bind constraints from config + options + DOM
    _bindConstraints: function () {
      var constraints = [], constraintsByName = {};

      // clean all existing DOM constraints to only keep javascript user constraints
      for (var i = 0; i < this.constraints.length; i++)
        if (false === this.constraints[i].isDomConstraint) {
          constraints.push(this.constraints[i]);
          constraintsByName[this.constraints[i].name] = this.constraints[i];
        }

      this.constraints = constraints;
      this.constraintsByName = constraintsByName;

      // then re-add Parsley DOM-API constraints
      for (var name in this.options)
        this.addConstraint(name, this.options[name]);

      // finally, bind special HTML5 constraints
      return this._bindHtml5Constraints();
    },

    // Internal only.
    // Bind specific HTML5 constraints to be HTML5 compliant
    _bindHtml5Constraints: function () {
      // html5 required
      if (this.$element.hasClass('required') || this.$element.attr('required'))
        this.addConstraint('required', true, undefined, true);

      // html5 pattern
      if ('string' === typeof this.$element.attr('pattern'))
        this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true);

      // range
      if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max'))
        this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true);

      // HTML5 min
      else if ('undefined' !== typeof this.$element.attr('min'))
        this.addConstraint('min', this.$element.attr('min'), undefined, true);

      // HTML5 max
      else if ('undefined' !== typeof this.$element.attr('max'))
        this.addConstraint('max', this.$element.attr('max'), undefined, true);

      // length
      if ('undefined' !== typeof this.$element.attr('minlength') && 'undefined' !== typeof this.$element.attr('maxlength'))
        this.addConstraint('length', [this.$element.attr('minlength'), this.$element.attr('maxlength')], undefined, true);

      // HTML5 minlength
      else if ('undefined' !== typeof this.$element.attr('minlength'))
        this.addConstraint('minlength', this.$element.attr('minlength'), undefined, true);

      // HTML5 maxlength
      else if ('undefined' !== typeof this.$element.attr('maxlength'))
        this.addConstraint('maxlength', this.$element.attr('maxlength'), undefined, true);

      // html5 types
      var type = this.$element.attr('type');

      if ('undefined' === typeof type)
        return this;

      // Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise
      if ('number' === type) {
        if (('undefined' === typeof this.$element.attr('step')) || (0 === parseFloat(this.$element.attr('step')) % 1)) {
          return this.addConstraint('type', 'integer', undefined, true);
        } else {
          return this.addConstraint('type', 'number', undefined, true);
        }
      // Regular other HTML5 supported types
      } else if (new RegExp(type, 'i').test('email url range')) {
        return this.addConstraint('type', type, undefined, true);
      }
      return this;
    },

    // Internal only.
    // Field is required if have required constraint without `false` value
    _isRequired: function () {
      if ('undefined' === typeof this.constraintsByName.required)
        return false;

      return false !== this.constraintsByName.required.requirements;
    },

    // Internal only.
    // Shortcut to trigger an event
    _trigger: function(event) {
      this.$element.trigger('field:' + event + '.parsley', [this]);
    },

    // Internal only.
    // Sort constraints by priority DESC
    _getConstraintsSortedPriorities: function () {
      var priorities = [];

      // Create array unique of priorities
      for (var i = 0; i < this.constraints.length; i++)
        if (-1 === priorities.indexOf(this.constraints[i].priority))
          priorities.push(this.constraints[i].priority);

      // Sort them by priority DESC
      priorities.sort(function (a, b) { return b - a; });

      return priorities;
    }
  };

  return ParsleyField;
});
<commit_msg>- removed !this.value in field isValid<commit_after>define('parsley/field', [
    'parsley/factory/constraint',
    'parsley/ui',
    'parsley/utils'
], function (ConstraintFactory, ParsleyUI, ParsleyUtils) {
  var ParsleyField = function (field, options, parsleyFormInstance) {
    this.__class__ = 'ParsleyField';
    this.__id__ = ParsleyUtils.hash(4);

    this.$element = $(field);

    // Set parent if we have one
    if ('undefined' !== typeof parsleyFormInstance) {
      this.parent = parsleyFormInstance;
    }

    this._resetOptions(options);

    // Initialize some properties
    this.constraints = [];
    this.constraintsByName = {};
    this.validationResult = [];

    // Bind constraints
    this._bindConstraints();
  };

  ParsleyField.prototype = {
    // # Public API
    // Validate field and trigger some events for mainly `ParsleyUI`
    // @returns validationResult:
    //  - `true` if field valid
    //  - `[Violation, [Violation...]]` if there were validation errors
    validate: function (force) {
      this.value = this.getValue();

      // Field Validate event. `this.value` could be altered for custom needs
      this._trigger('validate');

      this._trigger(this.isValid(force, this.value) ? 'success' : 'error');

      // Field validated event. `this.validationResult` could be altered for custom needs too
      this._trigger('validated');

      return this.validationResult;
    },

    hasConstraints: function () {
      return 0 !== this.constraints.length;
    },

    // An empty optional field does not need validation
    needsValidation: function (value) {
      if ('undefined' === typeof value)
        value = this.getValue();

      // If a field is empty and not required, it is valid
      // Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators
      if (!value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty)
        return false;

      return true;
    },

    // Just validate field. Do not trigger any event
    //  - `false` if there are constraints and at least one of them failed
    //  - `true` in all other cases
    isValid: function (force, value) {
      // Recompute options and rebind constraints to have latest changes
      this.refreshConstraints();
      this.validationResult = true;

      // A field without constraint is valid
      if (!this.hasConstraints())
        return true;

      // Value could be passed as argument, needed to add more power to 'parsley:field:validate'
      if ('undefined' === typeof value || null === value)
        value = this.getValue();

      if (!this.needsValidation(value) && true !== force)
        return true;

      // If we want to validate field against all constraints, just call Validator and let it do the job
      if (false === this.options.priorityEnabled)
        return true === (this.validationResult = this.validateThroughValidator(value, this.constraints, 'Any'));

      // Sort priorities to validate more important first
      var priorities = this._getConstraintsSortedPriorities();

      // Else, iterate over priorities one by one, and validate related asserts one by one
      for (var i = 0; i < priorities.length; i++)
        if (true !== (this.validationResult = this.validateThroughValidator(value, this.constraints, priorities[i])))
          return false;

      return true;
    },

    // @returns Parsley field computed value that could be overrided or configured in DOM
    getValue: function () {
      var value;

      // Value could be overriden in DOM or with explicit options
      if ('function' === typeof this.options.value)
        value = this.options.value(this);
      else if ('undefined' !== typeof this.options.value)
        value = this.options.value;
      else
        value = this.$element.val();

      // Handle wrong DOM or configurations
      if ('undefined' === typeof value || null === value)
        return '';

      // Use `data-parsley-trim-value="true"` to auto trim inputs entry
      if (true === this.options.trimValue)
        return value.replace(/^\s+|\s+$/g, '');

      return value;
    },

    // Actualize options that could have change since previous validation
    // Re-bind accordingly constraints (could be some new, removed or updated)
    refreshConstraints: function () {
      return this.actualizeOptions()._bindConstraints();
    },

    /**
    * Add a new constraint to a field
    *
    * @method addConstraint
    * @param {String}   name
    * @param {Mixed}    requirements      optional
    * @param {Number}   priority          optional
    * @param {Boolean}  isDomConstraint   optional
    */
    addConstraint: function (name, requirements, priority, isDomConstraint) {
      name = name.toLowerCase();

      if ('function' === typeof window.ParsleyValidator.validators[name]) {
        var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint);

        // if constraint already exist, delete it and push new version
        if ('undefined' !== this.constraintsByName[constraint.name])
          this.removeConstraint(constraint.name);

        this.constraints.push(constraint);
        this.constraintsByName[constraint.name] = constraint;
      }

      return this;
    },

    // Remove a constraint
    removeConstraint: function (name) {
      for (var i = 0; i < this.constraints.length; i++)
        if (name === this.constraints[i].name) {
          this.constraints.splice(i, 1);
          break;
        }
      delete this.constraintsByName[name];
      return this;
    },

    // Update a constraint (Remove + re-add)
    updateConstraint: function (name, parameters, priority) {
      return this.removeConstraint(name)
        .addConstraint(name, parameters, priority);
    },

    // # Internals

    // Internal only.
    // Bind constraints from config + options + DOM
    _bindConstraints: function () {
      var constraints = [], constraintsByName = {};

      // clean all existing DOM constraints to only keep javascript user constraints
      for (var i = 0; i < this.constraints.length; i++)
        if (false === this.constraints[i].isDomConstraint) {
          constraints.push(this.constraints[i]);
          constraintsByName[this.constraints[i].name] = this.constraints[i];
        }

      this.constraints = constraints;
      this.constraintsByName = constraintsByName;

      // then re-add Parsley DOM-API constraints
      for (var name in this.options)
        this.addConstraint(name, this.options[name]);

      // finally, bind special HTML5 constraints
      return this._bindHtml5Constraints();
    },

    // Internal only.
    // Bind specific HTML5 constraints to be HTML5 compliant
    _bindHtml5Constraints: function () {
      // html5 required
      if (this.$element.hasClass('required') || this.$element.attr('required'))
        this.addConstraint('required', true, undefined, true);

      // html5 pattern
      if ('string' === typeof this.$element.attr('pattern'))
        this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true);

      // range
      if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max'))
        this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true);

      // HTML5 min
      else if ('undefined' !== typeof this.$element.attr('min'))
        this.addConstraint('min', this.$element.attr('min'), undefined, true);

      // HTML5 max
      else if ('undefined' !== typeof this.$element.attr('max'))
        this.addConstraint('max', this.$element.attr('max'), undefined, true);

      // length
      if ('undefined' !== typeof this.$element.attr('minlength') && 'undefined' !== typeof this.$element.attr('maxlength'))
        this.addConstraint('length', [this.$element.attr('minlength'), this.$element.attr('maxlength')], undefined, true);

      // HTML5 minlength
      else if ('undefined' !== typeof this.$element.attr('minlength'))
        this.addConstraint('minlength', this.$element.attr('minlength'), undefined, true);

      // HTML5 maxlength
      else if ('undefined' !== typeof this.$element.attr('maxlength'))
        this.addConstraint('maxlength', this.$element.attr('maxlength'), undefined, true);

      // html5 types
      var type = this.$element.attr('type');

      if ('undefined' === typeof type)
        return this;

      // Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise
      if ('number' === type) {
        if (('undefined' === typeof this.$element.attr('step')) || (0 === parseFloat(this.$element.attr('step')) % 1)) {
          return this.addConstraint('type', 'integer', undefined, true);
        } else {
          return this.addConstraint('type', 'number', undefined, true);
        }
      // Regular other HTML5 supported types
      } else if (new RegExp(type, 'i').test('email url range')) {
        return this.addConstraint('type', type, undefined, true);
      }
      return this;
    },

    // Internal only.
    // Field is required if have required constraint without `false` value
    _isRequired: function () {
      if ('undefined' === typeof this.constraintsByName.required)
        return false;

      return false !== this.constraintsByName.required.requirements;
    },

    // Internal only.
    // Shortcut to trigger an event
    _trigger: function(event) {
      this.$element.trigger('field:' + event + '.parsley', [this]);
    },

    // Internal only.
    // Sort constraints by priority DESC
    _getConstraintsSortedPriorities: function () {
      var priorities = [];

      // Create array unique of priorities
      for (var i = 0; i < this.constraints.length; i++)
        if (-1 === priorities.indexOf(this.constraints[i].priority))
          priorities.push(this.constraints[i].priority);

      // Sort them by priority DESC
      priorities.sort(function (a, b) { return b - a; });

      return priorities;
    }
  };

  return ParsleyField;
});
<|endoftext|>