diff --git a/package.json b/package.json index 87c7a2c49..200aa2a99 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "babel-plugin-macros": "^2.6.1", "babel-plugin-transform-async-to-promises": "^0.8.14", "babel-plugin-transform-rename-import": "^2.3.0", + "babel-plugin-transform-replace-expressions": "^0.2.0", "babel-traverse": "^6.26.0", "babylon": "^6.18.0", "camelcase": "^5.0.0", diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index 816682e0c..ff887b0a1 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -1,8 +1,14 @@ -import { safeVariableName, safePackageName, external } from './utils'; +import { + safeVariableName, + safePackageName, + external, + toReplacementExpression, + parseMappingArgument, +} from './utils'; import { paths } from './constants'; import { terser } from 'rollup-plugin-terser'; import { DEFAULT_EXTENSIONS } from '@babel/core'; -// import babel from 'rollup-plugin-babel'; +import babel from 'rollup-plugin-babel'; import commonjs from 'rollup-plugin-commonjs'; import json from 'rollup-plugin-json'; import replace from 'rollup-plugin-replace'; @@ -39,6 +45,13 @@ export function createRollupConfig(opts: TsdxOptions) { .filter(Boolean) .join('.'); + let defines = {}; + if (opts.define) { + defines = Object.assign( + defines, + parseMappingArgument(opts.define, toReplacementExpression) + ); + } return { // Tell Rollup the entry point to the package input: opts.input, @@ -142,6 +155,20 @@ export function createRollupConfig(opts: TsdxOptions) { }, }, }), + // if defines is not set, we shouldn't run babel through node_modules + !!defines && + babel({ + babelrc: false, + configFile: false, + compact: false, + include: 'node_modules/**', + plugins: [ + [ + require.resolve('babel-plugin-transform-replace-expressions'), + { replace: defines }, + ], + ], + }), babelPluginTsdx({ exclude: 'node_modules/**', extensions: [...DEFAULT_EXTENSIONS, 'ts', 'tsx'], @@ -150,7 +177,7 @@ export function createRollupConfig(opts: TsdxOptions) { targets: opts.target === 'node' ? { node: '8' } : undefined, extractErrors: opts.extractErrors, format: opts.format, - // defines: opts.defines, + defines, }, }), opts.env !== undefined && @@ -174,6 +201,6 @@ export function createRollupConfig(opts: TsdxOptions) { toplevel: opts.format === 'cjs', warnings: true, }), - ], + ].filter(Boolean), }; } diff --git a/src/index.ts b/src/index.ts index 53eb7b076..56e87a2c0 100755 --- a/src/index.ts +++ b/src/index.ts @@ -316,8 +316,10 @@ prog .example('watch --verbose') .option('--tsconfig', 'Specify custom tsconfig path') .example('watch --tsconfig ./tsconfig.foo.json') - .option('--extractErrors', 'Extract invariant errors to ./errors/codes.json.') - .example('build --extractErrors') + .option('--extractErrors', 'Extract invariant errors to ./errors/codes.json') + .example('watch --extractErrors') + .option('--define', 'Replace constants with hard-coded values') + .example('watch --define A=1,ZOOP=FOO') .action(async (dirtyOpts: any) => { const opts = await normalizeOpts(dirtyOpts); const buildConfigs = createBuildConfigs(opts); @@ -375,13 +377,10 @@ prog .example('build --format cjs,esm') .option('--tsconfig', 'Specify custom tsconfig path') .example('build --tsconfig ./tsconfig.foo.json') - .option( - '--extractErrors', - 'Extract errors to ./errors/codes.json and provide a url for decoding.' - ) - .example( - 'build --extractErrors=https://reactjs.org/docs/error-decoder.html?invariant=' - ) + .option('--extractErrors', 'Extract errors to ./errors/codes.json') + .example('build --extractErrors') + .option('--define', 'Replace constants with hard-coded values') + .example('build --define A=1,ZOOP=FOO') .action(async (dirtyOpts: any) => { const opts = await normalizeOpts(dirtyOpts); const buildConfigs = createBuildConfigs(opts); diff --git a/src/types.ts b/src/types.ts index 907ea4550..e2acd5fe0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,4 +17,6 @@ export interface TsdxOptions { minify?: boolean; // Is this the very first rollup config (and thus should one-off metadata be extracted)? writeMeta?: boolean; + // Define key values to be replaced (e.g. --define A=1) + define?: string; } diff --git a/src/utils.ts b/src/utils.ts index 85385a83e..241395979 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -35,3 +35,44 @@ export function clearConsole() { process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H' ); } + +// Convert booleans and int define= values to literals. +// This is more intuitive than `microbundle --define A=1` producing A="1". +export const toReplacementExpression = (value: string, name: string) => { + // --define A="1",B='true' produces string: + const matches = value.match(/^(['"])(.+)\1$/); + if (matches) { + return [JSON.stringify(matches[2]), name]; + } + + // --define A=1,B=true produces int/boolean literal: + if (/^(true|false|\d+)$/i.test(value)) { + return [value, name]; + } + + // default: string literal + return [JSON.stringify(value), name]; +}; + +// Parses values of the form "$=jQuery,React=react" into key-value object pairs. +export const parseMappingArgument = ( + globalStrings: string, + processValue: (value: string, name: string) => any +) => { + const globals: any = {}; + globalStrings.split(',').forEach(globalString => { + let [key, value] = globalString.split('='); + if (processValue) { + const r = processValue(value, key); + if (r !== undefined) { + if (Array.isArray(r)) { + [value, key] = r; + } else { + value = r; + } + } + } + globals[key] = value; + }); + return globals; +}; diff --git a/yarn.lock b/yarn.lock index 888197e75..db0c5091e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -253,7 +253,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== -"@babel/parser@^7.4.5", "@babel/parser@^7.5.5": +"@babel/parser@^7.3.3", "@babel/parser@^7.4.5", "@babel/parser@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== @@ -1689,6 +1689,13 @@ babel-plugin-transform-rename-import@^2.3.0: resolved "https://registry.yarnpkg.com/babel-plugin-transform-rename-import/-/babel-plugin-transform-rename-import-2.3.0.tgz#5d9d645f937b0ca5c26a24b2510a06277b6ffd9b" integrity sha512-dPgJoT57XC0PqSnLgl2FwNvxFrWlspatX2dkk7yjKQj5HHGw071vAcOf+hqW8ClqcBDMvEbm6mevn5yHAD8mlQ== +babel-plugin-transform-replace-expressions@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-replace-expressions/-/babel-plugin-transform-replace-expressions-0.2.0.tgz#59cba8df4b4a675e7c78cd21548f8e7685bbc30d" + integrity sha512-Eh1rRd9hWEYgkgoA3D0kGp7xJ/wgVshgsqmq60iC4HVWD+Lux+fNHSHBa2v1Hsv+dHflShC71qKhiH40OiPtDA== + dependencies: + "@babel/parser" "^7.3.3" + babel-preset-jest@^24.6.0: version "24.6.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984"