diff --git a/package-lock.json b/package-lock.json index 6dcb39feda2..6c5fd584e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11837,9 +11837,9 @@ } }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz", + "integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -11869,6 +11869,17 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } } } }, diff --git a/package.json b/package.json index 54b6167ca27..e9198484b67 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "semiver": "^1.1.0", "semver": "7.3.2", "sizzle": "^2.3.5", - "terser": "4.8.0", + "terser": "5.1.0", "tslib": "^2.0.0", "typescript": "4.0.0-beta", "webpack": "^4.44.1", diff --git a/scripts/bundles/compiler.ts b/scripts/bundles/compiler.ts index 35137418acd..d17dd807b33 100644 --- a/scripts/bundles/compiler.ts +++ b/scripts/bundles/compiler.ts @@ -13,10 +13,11 @@ import { replacePlugin } from './plugins/replace-plugin'; import { sizzlePlugin } from './plugins/sizzle-plugin'; import { sysModulesPlugin } from './plugins/sys-modules-plugin'; import { writePkgJson } from '../utils/write-pkg-json'; -import { BuildOptions } from '../utils/options'; -import { RollupOptions, OutputChunk } from 'rollup'; +import type { BuildOptions } from '../utils/options'; +import type { RollupOptions, OutputChunk } from 'rollup'; import { typescriptSourcePlugin } from './plugins/typescript-source-plugin'; -import terser from 'terser'; +import { MinifyOptions, minify } from 'terser'; +import { terserPlugin } from './plugins/terser-plugin'; export async function compiler(opts: BuildOptions) { const inputDir = join(opts.buildDir, 'compiler'); @@ -56,6 +57,7 @@ export async function compiler(opts: BuildOptions) { }, plugins: [ typescriptSourcePlugin(opts), + terserPlugin(opts), { name: 'compilerMockDocResolvePlugin', resolveId(id) { @@ -115,7 +117,7 @@ export async function compiler(opts: BuildOptions) { if (opts.isProd) { const compilerFilename = Object.keys(bundleFiles).find(f => f.includes('stencil')); const compilerBundle = bundleFiles[compilerFilename] as OutputChunk; - const minified = minifyStencilCompiler(compilerBundle.code, opts); + const minified = await minifyStencilCompiler(compilerBundle.code, opts); await fs.writeFile(join(opts.output.compilerDir, compilerFilename.replace('.js', '.min.js')), minified); } }, @@ -142,8 +144,8 @@ export async function compiler(opts: BuildOptions) { return [compilerBundle]; } -function minifyStencilCompiler(code: string, opts: BuildOptions) { - const minifyOpts: terser.MinifyOptions = { +async function minifyStencilCompiler(code: string, opts: BuildOptions) { + const minifyOpts: MinifyOptions = { ecma: 2018, compress: { ecma: 2018, @@ -152,17 +154,13 @@ function minifyStencilCompiler(code: string, opts: BuildOptions) { unsafe_arrows: true, unsafe_methods: true, }, - output: { + format: { ecma: 2018, comments: false, }, }; - const results = terser.minify(code, minifyOpts); - - if (results.error) { - throw results.error; - } + const results = await minify(code, minifyOpts); code = getBanner(opts, `Stencil Compiler`, true) + '\n' + results.code; diff --git a/scripts/bundles/dev-server.ts b/scripts/bundles/dev-server.ts index bbf7bc1a8a0..e02ae770e62 100644 --- a/scripts/bundles/dev-server.ts +++ b/scripts/bundles/dev-server.ts @@ -7,9 +7,9 @@ import { aliasPlugin } from './plugins/alias-plugin'; import { relativePathPlugin } from './plugins/relative-path-plugin'; import { replacePlugin } from './plugins/replace-plugin'; import { writePkgJson } from '../utils/write-pkg-json'; -import { BuildOptions } from '../utils/options'; -import { RollupOptions, OutputChunk, Plugin } from 'rollup'; -import terser from 'terser'; +import type { BuildOptions } from '../utils/options'; +import type { RollupOptions, OutputChunk, Plugin } from 'rollup'; +import { minify } from 'terser'; import ts from 'typescript'; import { prettyMinifyPlugin } from './plugins/pretty-minify'; @@ -145,7 +145,7 @@ export async function devServer(opts: BuildOptions) { appErrorCssPlugin(), { name: 'clientConnectorPlugin', - generateBundle(_options, bundle) { + async generateBundle(_options, bundle) { if (bundle[connectorName]) { let code = (bundle[connectorName] as OutputChunk).code; @@ -164,13 +164,10 @@ export async function devServer(opts: BuildOptions) { code = intro + code + outro; if (opts.isProd) { - const minifyResults = terser.minify(code, { + const minifyResults = await minify(code, { compress: { hoist_vars: true, hoist_funs: true, ecma: 5 }, - output: { ecma: 5 }, + format: { ecma: 5 }, }); - if (minifyResults.error) { - throw minifyResults.error; - } code = minifyResults.code; } diff --git a/scripts/bundles/internal-platform-client.ts b/scripts/bundles/internal-platform-client.ts index 4588e00cf20..a78519168d0 100644 --- a/scripts/bundles/internal-platform-client.ts +++ b/scripts/bundles/internal-platform-client.ts @@ -1,6 +1,6 @@ import fs from 'fs-extra'; import { basename, join } from 'path'; -import { BuildOptions } from '../utils/options'; +import type { BuildOptions } from '../utils/options'; import { aliasPlugin } from './plugins/alias-plugin'; import { replacePlugin } from './plugins/replace-plugin'; import { reorderCoreStatementsPlugin } from './plugins/reorder-statements'; @@ -114,7 +114,7 @@ export async function internalClient(opts: BuildOptions) { let code = transpileToEs5.outputText; if (opts.isProd) { - const minifyResults = minify(code); + const minifyResults = await minify(code); code = minifyResults.code; } diff --git a/scripts/bundles/plugins/parse5-plugin.ts b/scripts/bundles/plugins/parse5-plugin.ts index 043c990270c..d31bb78b8af 100644 --- a/scripts/bundles/plugins/parse5-plugin.ts +++ b/scripts/bundles/plugins/parse5-plugin.ts @@ -1,11 +1,11 @@ import fs from 'fs-extra'; import { aliasPlugin } from './alias-plugin'; import { join } from 'path'; -import { BuildOptions } from '../../utils/options'; +import type { BuildOptions } from '../../utils/options'; import rollupCommonjs from '@rollup/plugin-commonjs'; import rollupResolve from '@rollup/plugin-node-resolve'; import { rollup, OutputChunk, Plugin } from 'rollup'; -import terser from 'terser'; +import { minify } from 'terser'; export function parse5Plugin(opts: BuildOptions): Plugin { return { @@ -77,22 +77,18 @@ async function bundleParse5(opts: BuildOptions) { let code = output[0].code; if (opts.isProd) { - const minified = terser.minify(code, { + const minified = await minify(code, { ecma: 2018, module: true, compress: { ecma: 2018, passes: 2, }, - output: { + format: { ecma: 2018, comments: false, }, }); - - if (minified.error) { - throw minified.error; - } code = minified.code; } diff --git a/scripts/bundles/plugins/pretty-minify.ts b/scripts/bundles/plugins/pretty-minify.ts index 5eeb90be82a..f1a366f18cf 100644 --- a/scripts/bundles/plugins/pretty-minify.ts +++ b/scripts/bundles/plugins/pretty-minify.ts @@ -1,26 +1,25 @@ -import { BuildOptions } from '../../utils/options'; -import { Plugin, OutputChunk } from 'rollup'; -import terser from 'terser'; +import type { BuildOptions } from '../../utils/options'; +import type { Plugin, OutputChunk } from 'rollup'; +import { minify } from 'terser'; export function prettyMinifyPlugin(opts: BuildOptions, preamble?: string): Plugin { if (opts.isProd) { return { name: 'prettyMinifyPlugin', - generateBundle(_, bundles) { - Object.keys(bundles).forEach(fileName => { - const b = bundles[fileName] as OutputChunk; - if (typeof b.code === 'string') { - const minifyResults = terser.minify(b.code, { - compress: { hoist_vars: true, hoist_funs: true, ecma: 2018, keep_fnames: true, keep_classnames: true, module: true, arrows: true, passes: 2 }, - output: { ecma: 2018, indent_level: 1, beautify: true, comments: false, preamble }, - sourceMap: false, - }); - if (minifyResults.error) { - throw minifyResults.error; + async generateBundle(_, bundles) { + await Promise.all( + Object.keys(bundles).map(async fileName => { + const b = bundles[fileName] as OutputChunk; + if (typeof b.code === 'string') { + const minifyResults = await minify(b.code, { + compress: { hoist_vars: true, hoist_funs: true, ecma: 2018, keep_fnames: true, keep_classnames: true, module: true, arrows: true, passes: 2 }, + format: { ecma: 2018, indent_level: 1, beautify: true, comments: false, preamble }, + sourceMap: false, + }); + b.code = minifyResults.code; } - b.code = minifyResults.code; - } - }); + }), + ); }, }; } diff --git a/scripts/bundles/plugins/terser-plugin.ts b/scripts/bundles/plugins/terser-plugin.ts new file mode 100644 index 00000000000..22255493249 --- /dev/null +++ b/scripts/bundles/plugins/terser-plugin.ts @@ -0,0 +1,64 @@ +import fs from 'fs-extra'; +import { join } from 'path'; +import type { BuildOptions } from '../../utils/options'; +import { rollup, Plugin } from 'rollup'; +import { minify } from 'terser'; + +export function terserPlugin(opts: BuildOptions): Plugin { + return { + name: 'terserPlugin', + resolveId(id) { + if (id === 'terser') { + return id; + } + return null; + }, + async load(id) { + if (id === 'terser') { + return await bundleTerser(opts); + } + return null; + }, + }; +} + +async function bundleTerser(opts: BuildOptions) { + const fileName = `terser-${opts.terserVersion.replace(/\./g, '_')}-bundle-cache${opts.isProd ? '.min' : ''}.js`; + const cacheFile = join(opts.scriptsBuildDir, fileName); + + try { + return await fs.readFile(cacheFile, 'utf8'); + } catch (e) {} + + const rollupBuild = await rollup({ + input: join(opts.nodeModulesDir, 'terser', 'main.js'), + external: ['source-map'], + }); + + const { output } = await rollupBuild.generate({ + format: 'es', + preferConst: true, + strict: false, + }); + + let code = output[0].code; + + if (opts.isProd) { + const minified = await minify(code, { + ecma: 2018, + compress: { + ecma: 2018, + passes: 2, + }, + format: { + ecma: 2018, + comments: false, + }, + }); + code = minified.code; + } + + await fs.writeFile(cacheFile, code); + + return code; +} diff --git a/scripts/bundles/plugins/typescript-source-plugin.ts b/scripts/bundles/plugins/typescript-source-plugin.ts index 1ca93991cce..ca5b4249952 100644 --- a/scripts/bundles/plugins/typescript-source-plugin.ts +++ b/scripts/bundles/plugins/typescript-source-plugin.ts @@ -1,8 +1,8 @@ import fs from 'fs-extra'; -import { Plugin } from 'rollup'; +import type { Plugin } from 'rollup'; import { join } from 'path'; -import { BuildOptions } from '../../utils/options'; -import terser from 'terser'; +import type { BuildOptions } from '../../utils/options'; +import { minify } from 'terser'; export function typescriptSourcePlugin(opts: BuildOptions): Plugin { const tsPath = require.resolve('typescript'); @@ -73,22 +73,18 @@ async function bundleTypeScriptSource(tsPath: string, opts: BuildOptions) { code = o.join('\n'); if (opts.isProd) { - const minified = terser.minify(code, { + const minified = await minify(code, { ecma: 2018, module: true, compress: { ecma: 2018, passes: 2, }, - output: { + format: { ecma: 2018, comments: false, }, }); - - if (minified.error) { - throw minified.error; - } code = minified.code; } diff --git a/scripts/bundles/sys-node.ts b/scripts/bundles/sys-node.ts index 11d4e00fba6..ce1a9653680 100644 --- a/scripts/bundles/sys-node.ts +++ b/scripts/bundles/sys-node.ts @@ -1,11 +1,11 @@ import fs from 'fs-extra'; import { join } from 'path'; import webpack from 'webpack'; -import terser from 'terser'; +import { minify } from 'terser'; import rollupCommonjs from '@rollup/plugin-commonjs'; import rollupResolve from '@rollup/plugin-node-resolve'; -import { BuildOptions } from '../utils/options'; -import { RollupOptions } from 'rollup'; +import type { BuildOptions } from '../utils/options'; +import type { RollupOptions } from 'rollup'; import { relativePathPlugin } from './plugins/relative-path-plugin'; import { aliasPlugin } from './plugins/alias-plugin'; import { prettyMinifyPlugin } from './plugins/pretty-minify'; @@ -191,12 +191,13 @@ function bundleExternal(opts: BuildOptions, outputDir: string, cachedDir: string let code = await fs.readFile(outputFile, 'utf8'); if (opts.isProd) { - const minifyResults = terser.minify(code); - if (minifyResults.error) { - rejectBundle(minifyResults.error); + try { + const minifyResults = await minify(code); + code = minifyResults.code; + } catch (e) { + rejectBundle(e); return; } - code = minifyResults.code; } await fs.writeFile(cachedFile, code); await fs.writeFile(outputFile, code); diff --git a/src/compiler/config/validate-config.ts b/src/compiler/config/validate-config.ts index 46532faf256..9174c1078ab 100644 --- a/src/compiler/config/validate-config.ts +++ b/src/compiler/config/validate-config.ts @@ -47,8 +47,6 @@ export const validateConfig = (userConfig?: Config) => { setBooleanConfig(config, 'minifyJs', null, !config.devMode); setBooleanConfig(config, 'sourceMap', null, false); setBooleanConfig(config, 'watch', 'watch', false); - setBooleanConfig(config, 'minifyCss', null, !config.devMode); - setBooleanConfig(config, 'minifyJs', null, !config.devMode); setBooleanConfig(config, 'buildDocs', 'docs', !config.devMode); setBooleanConfig(config, 'buildDist', 'esm', !config.devMode || config.buildEs5); setBooleanConfig(config, 'profile', 'profile', config.devMode); diff --git a/src/compiler/optimize/minify-js.ts b/src/compiler/optimize/minify-js.ts index 3de72784dec..c0a9ad1509e 100644 --- a/src/compiler/optimize/minify-js.ts +++ b/src/compiler/optimize/minify-js.ts @@ -1,8 +1,14 @@ import type * as d from '../../declarations'; import { splitLineBreaks } from '@utils'; -import terser, { CompressOptions, MangleOptions, ManglePropertiesOptions, MinifyOptions } from 'terser'; +import { CompressOptions, MangleOptions, ManglePropertiesOptions, MinifyOptions, minify } from 'terser'; export const minifyJs = async (input: string, opts?: MinifyOptions) => { + const results = { + output: input, + sourceMap: null as any, + diagnostics: [] as d.Diagnostic[], + }; + if (opts) { const mangle = opts.mangle as MangleOptions; if (mangle) { @@ -12,36 +18,32 @@ export const minifyJs = async (input: string, opts?: MinifyOptions) => { } } } - const result = terser.minify(input, opts); - const diagnostics = loadMinifyJsDiagnostics(input, result); - if (diagnostics.length === 0) { + try { + const minifyResults = await minify(input, opts); + + results.output = minifyResults.code; + const compress = opts.compress as CompressOptions; - if (compress && compress.module && result.code.endsWith('};')) { - result.code = result.code.substring(0, result.code.length - 1); + if (compress && compress.module && results.output.endsWith('};')) { + results.output = results.output.substring(0, results.output.length - 1); } + } catch (e) { + console.log(e.stack) + loadMinifyJsDiagnostics(input, results.diagnostics, e); } - return { - output: result.code, - sourceMap: result.map as any, - diagnostics: diagnostics, - }; + return results; }; -const loadMinifyJsDiagnostics = (sourceText: string, result: terser.MinifyOutput) => { - const diagnostics: d.Diagnostic[] = []; - if (!result || !result.error) { - return diagnostics; - } - +const loadMinifyJsDiagnostics = (sourceText: string, diagnostics: d.Diagnostic[], error: any) => { const d: d.Diagnostic = { level: 'error', type: 'build', language: 'javascript', header: 'Minify JS', code: '', - messageText: result.error.message, + messageText: error.message, absFilePath: null, relFilePath: null, lines: [], @@ -55,7 +57,7 @@ const loadMinifyJsDiagnostics = (sourceText: string, result: terser.MinifyOutput name: string; pos: number; stack: string; - } = result.error as any; + } = error; if (typeof err.line === 'number' && err.line > -1) { const srcLines = splitLineBreaks(sourceText); @@ -73,7 +75,7 @@ const loadMinifyJsDiagnostics = (sourceText: string, result: terser.MinifyOutput const highlightLine = errorLine.text.substr(d.columnNumber); for (let i = 0; i < highlightLine.length; i++) { - if (CHAR_BREAK.has(highlightLine.charAt(i))) { + if (MINIFY_CHAR_BREAK.has(highlightLine.charAt(i))) { break; } errorLine.errorLength++; @@ -112,8 +114,6 @@ const loadMinifyJsDiagnostics = (sourceText: string, result: terser.MinifyOutput } diagnostics.push(d); - - return diagnostics; }; -const CHAR_BREAK = new Set([' ', '=', '.', ',', '?', ':', ';', '(', ')', '{', '}', '[', ']', '|', `'`, `"`, '`']); +const MINIFY_CHAR_BREAK = new Set([' ', '=', '.', ',', '?', ':', ';', '(', ')', '{', '}', '[', ']', '|', `'`, `"`, '`']); diff --git a/src/compiler/optimize/optimize-module.ts b/src/compiler/optimize/optimize-module.ts index 3218bce6c3a..dee22195818 100644 --- a/src/compiler/optimize/optimize-module.ts +++ b/src/compiler/optimize/optimize-module.ts @@ -38,7 +38,7 @@ export const optimizeModule = async (config: Config, compilerCtx: CompilerCtx, o code = code.replace(/\/\* IS_ESM_BUILD \*\//g, '&& false /* IS_SYSTEM_JS_BUILD */'); } - if (opts.sourceTarget === 'es5' && opts.minify) { + if (opts.sourceTarget === 'es5' || opts.minify) { minifyOpts = getTerserOptions(config, opts.sourceTarget, isDebug); const compressOpts = minifyOpts.compress as CompressOptions; const mangleOptions = minifyOpts.mangle as MangleOptions; @@ -81,11 +81,11 @@ export const getTerserOptions = (config: Config, sourceTarget: SourceTarget, pre const opts: MinifyOptions = { ie8: false, safari10: !!config.extras.safari10, - output: {}, + format: {}, }; if (sourceTarget === 'es5') { - opts.ecma = opts.output.ecma = 5; + opts.ecma = opts.format.ecma = 5; opts.compress = false; opts.mangle = true; } else { @@ -100,7 +100,7 @@ export const getTerserOptions = (config: Config, sourceTarget: SourceTarget, pre passes: 2, }; - opts.ecma = opts.output.ecma = opts.compress.ecma = 8; + opts.ecma = opts.format.ecma = opts.compress.ecma = 2018; opts.toplevel = true; opts.module = true; opts.mangle.toplevel = true; @@ -115,9 +115,9 @@ export const getTerserOptions = (config: Config, sourceTarget: SourceTarget, pre opts.compress.drop_console = false; opts.compress.drop_debugger = false; opts.compress.pure_funcs = []; - opts.output.beautify = true; - opts.output.indent_level = 2; - opts.output.comments = 'all'; + opts.format.beautify = true; + opts.format.indent_level = 2; + opts.format.comments = 'all'; } return opts;