diff --git a/bin/elm-optimize-level-2.js b/bin/elm-optimize-level-2.js index df107c6..260d4f2 100755 --- a/bin/elm-optimize-level-2.js +++ b/bin/elm-optimize-level-2.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -const index = require('../dist/index.js'); +const index = require('../dist/bin.js'); diff --git a/src/bin.ts b/src/bin.ts new file mode 100644 index 0000000..95162d1 --- /dev/null +++ b/src/bin.ts @@ -0,0 +1,49 @@ +import program from 'commander'; +import chalk from 'chalk'; +import { run } from './run'; +const { version } = require('../package.json'); +// import * as BenchInit from './benchmark/init' +// import * as Benchmark from './benchmark/benchmark'; +// import * as Reporting from './benchmark/reporting'; + +program + .version(version) + .description( + `${chalk.yellow('Elm Optimize Level 2!')} + +This applies a second level of optimization to the javascript that Elm creates. + +Make sure you're familiar with Elm's built-in optimization first: ${chalk.cyan( + 'https://guide.elm-lang.org/optimization/asset_size.html' + )} + +Give me an Elm file, I'll compile it behind the scenes using Elm 0.19.1, and then I'll make some more optimizations!` + ) + .usage('[options] ') + .option('--output ', 'the javascript file to create.', 'elm.js') + .option('-O3, --optimize-speed', 'Enable optimizations that likely increases asset size', false) + .option('--verbose', 'Show more error details, useful to provide better bug reports', false) + // .option('--init-benchmark ', 'Generate some files to help run benchmarks') + // .option('--benchmark ', 'Run the benchmark in the given directory.') + // .option('--replacements ', 'Replace stuff') + .parse(process.argv); + +const { output, optimizeSpeed, verbose } = program.opts(); +run( + { + inputFilePath: program.args[0], + outputFilePath: output, + optimizeSpeed, + verbose, + processOpts: { stdio: ['inherit', 'ignore', 'inherit'] }, + }, + program.helpInformation(), + console.log.bind(console), +).catch((e) => { + if (verbose) { + console.error(e); + } else { + console.error(e.toString()); + } + process.exit(1); +}); diff --git a/src/index.ts b/src/index.ts index 7b3b912..74c0594 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,127 +1,20 @@ -// tslint:disable-next-line no-require-imports no-var-requires -import program from 'commander'; -import * as path from 'path'; -import * as Transform from './transform'; -import { toolDefaults } from './types'; -import { compileToStringSync } from 'node-elm-compiler'; -import * as fs from 'fs'; -import chalk from 'chalk'; -const { version } = require('../package.json'); -// import * as BenchInit from './benchmark/init' -// import * as Benchmark from './benchmark/benchmark'; -// import * as Reporting from './benchmark/reporting'; +import * as Run from './run'; -program - .version(version) - .description( - `${chalk.yellow('Elm Optimize Level 2!')} - -This applies a second level of optimization to the javascript that Elm creates. - -Make sure you're familiar with Elm's built-in optimization first: ${chalk.cyan( - 'https://guide.elm-lang.org/optimization/asset_size.html' - )} - -Give me an Elm file, I'll compile it behind the scenes using Elm 0.19.1, and then I'll make some more optimizations!` - ) - .usage('[options] ') - .option('--output ', 'the javascript file to create.', 'elm.js') - .option('-O3, --optimize-speed', 'Enable optimizations that likely increases asset size', false) - // .option('--init-benchmark ', 'Generate some files to help run benchmarks') - // .option('--benchmark ', 'Run the benchmark in the given directory.') - // .option('--replacements ', 'Replace stuff') - .parse(process.argv); - -async function run(inputFilePath: string | undefined) { - const dirname = process.cwd(); - let jsSource: string = ''; - let elmFilePath = undefined; - - const options = program.opts(); - const replacements = null; - const o3Enabled = options.optimizeSpeed; - - // if (program.initBenchmark) { - // console.log(`Initializing benchmark ${program.initBenchmark}`) - // BenchInit.generate(program.initBenchmark) - // process.exit(0) - // } - -// if (program.benchmark) { -// const options = { -// compile: true, -// gzip: true, -// minify: true, -// verbose: true, -// assetSizes: true, -// runBenchmark: [ -// { -// browser: Browser.Chrome, -// headless: true, -// } -// ], -// transforms: benchmarkDefaults(o3Enabled, replacements), -// }; -// const report = await Benchmark.run(options, [ -// { -// name: 'Benchmark', -// dir: program.benchmark, -// elmFile: 'V8/Benchmark.elm', -// } -// ]); -// console.log(Reporting.terminal(report)); -// // fs.writeFileSync('./results.markdown', Reporting.markdownTable(result)); -// process.exit(0) -// } - - if (inputFilePath && inputFilePath.endsWith('.js')) { - jsSource = fs.readFileSync(inputFilePath, 'utf8'); - console.log('Optimizing existing JS...'); - } else if (inputFilePath && inputFilePath.endsWith('.elm')) { - elmFilePath = inputFilePath; - jsSource = compileToStringSync([inputFilePath], { - output: 'output/elm.opt.js', - cwd: dirname, - optimize: true, - processOpts: - // ignore stdout - { - stdio: ['inherit', 'ignore', 'inherit'], - }, - }); - if (jsSource != '') { - console.log('Compiled, optimizing JS...'); - } else { - process.exit(1) - } - } else { - console.error('Please provide a path to an Elm file.'); - program.outputHelp(); - return; - } - if (jsSource != '') { - const transformed = await Transform.transform( - dirname, - jsSource, - elmFilePath, - false, - toolDefaults(o3Enabled, replacements), - ); - - // Make sure all the folders up to the output file exist, if not create them. - // This mirrors elm make behavior. - const outputDirectory = path.dirname(program.output); - if (!fs.existsSync(outputDirectory)) { - fs.mkdirSync(outputDirectory, { recursive: true }); - } - fs.writeFileSync(program.output, transformed); - const fileName = path.basename(inputFilePath); - console.log('Success!'); - console.log(''); - console.log(` ${fileName} ───> ${program.output}`); - console.log(''); - } +export async function run(options: { + inputFilePath: string | undefined, + outputFilePath: string, + optimizeSpeed: boolean, + processOpts: { + stdio: [string, string, string], + } | null, +}) { + return Run.run( + { + ...options, + verbose: false, + processOpts: options.processOpts || { stdio: ['inherit', 'ignore', 'inherit'] }, + }, + '', + () => { } + ); } - - -run(program.args[0]).catch((e) => console.error(e)); diff --git a/src/run.ts b/src/run.ts new file mode 100644 index 0000000..963e395 --- /dev/null +++ b/src/run.ts @@ -0,0 +1,115 @@ +import * as path from 'path'; +import * as Transform from './transform'; +import { toolDefaults } from './types'; +import { compileToStringSync } from 'node-elm-compiler'; +import * as fs from 'fs'; +// import * as BenchInit from './benchmark/init' +// import * as Benchmark from './benchmark/benchmark'; +// import * as Reporting from './benchmark/reporting'; + +export async function run( + options: { + inputFilePath: string | undefined, + outputFilePath: string, + optimizeSpeed: boolean, + verbose: boolean, + processOpts: { stdio: [string, string, string] }, + }, + helpInformation: string, + log: (message?: any, ...optionalParams: any[]) => void +) { + if (!options.outputFilePath) { + throw new Error('Missing an output file path'); + } + + const dirname = process.cwd(); + let jsSource: string = ''; + let elmFilePath = undefined; + + const replacements = null; + const inputFilePath = options.inputFilePath; + const o3Enabled = options.optimizeSpeed; + + // if (program.initBenchmark) { + // console.log(`Initializing benchmark ${program.initBenchmark}`) + // BenchInit.generate(program.initBenchmark) + // process.exit(0) + // } + + // if (program.benchmark) { + // const options = { + // compile: true, + // gzip: true, + // minify: true, + // verbose: true, + // assetSizes: true, + // runBenchmark: [ + // { + // browser: Browser.Chrome, + // headless: true, + // } + // ], + // transforms: benchmarkDefaults(o3Enabled, replacements), + // }; + // const report = await Benchmark.run(options, [ + // { + // name: 'Benchmark', + // dir: program.benchmark, + // elmFile: 'V8/Benchmark.elm', + // } + // ]); + // console.log(Reporting.terminal(report)); + // // fs.writeFileSync('./results.markdown', Reporting.markdownTable(result)); + // process.exit(0) + // } + + if (inputFilePath && inputFilePath.endsWith('.js')) { + jsSource = fs.readFileSync(inputFilePath, 'utf8'); + log('Optimizing existing JS...'); + } else if (inputFilePath && inputFilePath.endsWith('.elm')) { + elmFilePath = inputFilePath; + jsSource = compileToStringSync([inputFilePath], { + output: 'output/elm.opt.js', + cwd: dirname, + optimize: true, + processOpts: + // ignore stdout + { + stdio: ['inherit', 'ignore', 'inherit'], + }, + }); + if (jsSource != '') { + log('Compiled, optimizing JS...'); + } else { + throw new Error('An error occurred when compiling your application with Elm 0.19.1.'); + } + } else { + throw new Error(`Please provide a path to an Elm file.\n${helpInformation}`.trim()); + } + + if (jsSource == '') { + throw new Error('Target JS file is empty.'); + } + + const transformed = await Transform.transform( + dirname, + jsSource, + elmFilePath, + options.verbose, + toolDefaults(o3Enabled, replacements), + ); + + // Make sure all the folders up to the output file exist, if not create them. + // This mirrors elm make behavior. + const outputDirectory = path.dirname(options.outputFilePath); + if (!fs.existsSync(outputDirectory)) { + fs.mkdirSync(outputDirectory, { recursive: true }); + } + fs.writeFileSync(options.outputFilePath, transformed); + const fileName = path.basename(inputFilePath); + log('Success!'); + log(''); + log(` ${fileName} ───> ${options.outputFilePath}`); + log(''); + return options.outputFilePath; +}