From 374926e10585218557c10b6db56d03183db06bac Mon Sep 17 00:00:00 2001 From: Yisheng Jiang Date: Thu, 1 Oct 2020 15:17:43 -0700 Subject: [PATCH] allow user to pass in all options when loading xrun build tasks (#1739) --- packages/xarc-app-dev/src/config/archetype.ts | 70 +++++++-- .../xarc-app-dev/src/config/babel/babelrc.ts | 6 +- .../src/config/opt2/add-on-features.ts | 46 ++++++ .../src/config/opt2/babel-options.ts | 63 ++++++++ .../src/config/opt2/karma-options.ts | 15 ++ .../src/config/opt2/webpack-options.ts | 141 ++++++++++++++++++ .../src/config/opt2/xarc-options.ts | 80 ++++++++++ packages/xarc-app-dev/src/config/options.ts | 3 +- .../xarc-app-dev/src/config/user-config.ts | 3 + .../xarc-app-dev/src/lib/load-xrun-tasks.ts | 2 - packages/xarc-app-dev/src/lib/utils.ts | 1 - packages/xarc-app-dev/src/typedef.js | 8 - packages/xarc-app/lib/app-mode.js | 2 - 13 files changed, 410 insertions(+), 30 deletions(-) create mode 100644 packages/xarc-app-dev/src/config/opt2/add-on-features.ts create mode 100644 packages/xarc-app-dev/src/config/opt2/babel-options.ts create mode 100644 packages/xarc-app-dev/src/config/opt2/karma-options.ts create mode 100644 packages/xarc-app-dev/src/config/opt2/webpack-options.ts create mode 100644 packages/xarc-app-dev/src/config/opt2/xarc-options.ts delete mode 100644 packages/xarc-app-dev/src/typedef.js diff --git a/packages/xarc-app-dev/src/config/archetype.ts b/packages/xarc-app-dev/src/config/archetype.ts index 1ae0d4452..478cdce12 100644 --- a/packages/xarc-app-dev/src/config/archetype.ts +++ b/packages/xarc-app-dev/src/config/archetype.ts @@ -1,24 +1,22 @@ /* eslint-disable @typescript-eslint/no-var-requires, max-statements */ +import { XarcOptions } from "./opt2/xarc-options"; + const Path = require("path"); const { merge } = require("lodash"); const { getXarcOptions, getMyPkg } = require("../lib/utils"); const constants = require("./constants"); +const Fs = require("fs"); const _ = require("lodash"); const xenvConfig = require("xenv-config"); const makeAppMode = require("@xarc/app/lib/app-mode"); const { getDefaultArchetypeOptions } = require("./options"); +const getEnvProxy = require("./env-proxy"); let cachedArchetype = null; -module.exports = function getDevArchetype(createXarcOptions) { - if (cachedArchetype) { - cachedArchetype._fromCache = true; - // maintained for backwards compatibility - return cachedArchetype; - } - +function getDevArchetypeLegacy(createXarcOptions) { const xarcOptions = getXarcOptions(createXarcOptions); const defaultArchetypeConfig = getDefaultArchetypeOptions(xarcOptions); const userConfig = defaultArchetypeConfig.options; @@ -58,12 +56,10 @@ module.exports = function getDevArchetype(createXarcOptions) { devOpenBrowser: { env: "ELECTRODE_DEV_OPEN_BROWSER", default: false } }; - const { options } = userConfig; - const typeScriptOption = - options.typescript === false + userConfig.typescript === false ? { - babel: { enableTypeScript: options.typescript } + babel: { enableTypeScript: userConfig.typescript } } : {}; @@ -93,7 +89,57 @@ module.exports = function getDevArchetype(createXarcOptions) { } }); - cachedArchetype = archetypeConfig; + return archetypeConfig; +} + +function saveArchetypeConfig(config) { + const copy = { ...config, pkg: undefined, devPkg: undefined }; + let existStr; + + try { + existStr = Fs.readFileSync(".etmp/config-options.json", "utf-8"); + } catch (err) { + // + } + + const str = JSON.stringify(copy, null, 2); + if (str !== existStr) { + Fs.writeFileSync(".etmp/config-options.json", str); + } +} + +module.exports = function getDevArchetype(user: XarcOptions = {}) { + if (cachedArchetype) { + cachedArchetype._fromCache = true; + // maintained for backwards compatibility + return cachedArchetype; + } + + // first get legacy configs + const legacy = getDevArchetypeLegacy({}); + + const proxy = getEnvProxy(); + + // proxy config was not set in legacy, so add to top level here + _.merge(legacy, proxy); + + // merge user.webpackOptions into legacy.webpack + _.merge(legacy.webpack, user.webpackOptions); + // merge user.babelOptions into legacy.babel + _.merge(legacy.babel, user.babelOptions); + // merge user.addOnFeatures into legacy.options + _.merge(legacy.options, user.addOnFeatures); + // merge the rest into top level + _.merge(legacy, { + ...user, + webpackOptions: undefined, + babelOptions: undefined, + addOnFeatures: undefined + }); + + saveArchetypeConfig(legacy); + + cachedArchetype = legacy; return cachedArchetype; }; diff --git a/packages/xarc-app-dev/src/config/babel/babelrc.ts b/packages/xarc-app-dev/src/config/babel/babelrc.ts index 4d80f6d37..6c80dd2c9 100644 --- a/packages/xarc-app-dev/src/config/babel/babelrc.ts +++ b/packages/xarc-app-dev/src/config/babel/babelrc.ts @@ -106,7 +106,7 @@ const basePlugins = [ // Note: This must go before @babel/plugin-proposal-class-properties (enableTypeScript || proposalDecorators) && [ "@babel/plugin-proposal-decorators", - { legacy: legacyDecorators } + { legacy: legacyDecorators, ...proposalDecorators } ], // // allow class properties. loose option compile to assignment expression instead @@ -115,7 +115,7 @@ const basePlugins = [ // (enableTypeScript || transformClassProps) && [ "@babel/plugin-proposal-class-properties", - { loose: looseClassProps } + { loose: looseClassProps, ...transformClassProps } ], // // i18n has not been used at all and these are very outdated @@ -139,7 +139,7 @@ const basePlugins = [ !isNodeTarget && "@babel/plugin-transform-runtime", addFlowPlugin && [ "@babel/plugin-transform-flow-strip-types", - { requireDirective: flowRequireDirective } + { requireDirective: flowRequireDirective, ...enableFlow } ] ]; diff --git a/packages/xarc-app-dev/src/config/opt2/add-on-features.ts b/packages/xarc-app-dev/src/config/opt2/add-on-features.ts new file mode 100644 index 000000000..1b652f113 --- /dev/null +++ b/packages/xarc-app-dev/src/config/opt2/add-on-features.ts @@ -0,0 +1,46 @@ +/** + * Optional features to support + */ +export type AddOnFeatures = { + /** Enable flow.js support? **Default: `false`** */ + flow?: boolean; + + /** + * Enable xarc's built-in eslint checks + * - This is enabled if you add `@xarc/opt-eslint` to your devDependencies + */ + eslint?: boolean; + + /** + * Enable support for running function tests with Karma + * - **Default: `false`** + * - This is enabled if you add `@xarc/opt-karma` to your devDependencies + */ + karma?: boolean; + + /** + * Enable support for running test with jest + * - This is enabled if you add `@xarc/opt-jest` to your devDependencies + */ + jest?: boolean; + + /** + * Enable support for running tests with mocha + * - This is enabled if you add `@xarc/opt-mocha` to your devDependencies + */ + mocha?: boolean; + + /** + * Select an implementation of the React UI framework **Default: `react`** + */ + reactLib?: "react" | "preact" | "inferno"; + + /** Enable support for using TypeScript */ + typescript?: boolean; + + /** + * Enable support for sass styling + * - This is enabled if you add `@xarc/opt-sass` to your devDependencies + */ + sass?: boolean; +}; diff --git a/packages/xarc-app-dev/src/config/opt2/babel-options.ts b/packages/xarc-app-dev/src/config/opt2/babel-options.ts new file mode 100644 index 000000000..23243d545 --- /dev/null +++ b/packages/xarc-app-dev/src/config/opt2/babel-options.ts @@ -0,0 +1,63 @@ +// options from ../env-babel.ts + +/** + * configurable options related to transpiler (babel) + */ +export type BabelOptions = { + /** + * enable support for typescript types using `@babel/preset-typescript` + * + * @remarks + * transpile only, no type checking + * + * - **Default: true** + * - if not set, then we check env `ENABLE_BABEL_TYPESCRIPT` + */ + enableTypeScript?: boolean; + // DEPRECATE: enableDynamicImport?: boolean; + + /** + * Enable support for stripping flow.js types using `@babel/plugin-transform-flow-strip-types` + * + * @remarks + * transpile only, no type checking + * + * - **Default: `false`** + * - if not set, then we check env `ENABLE_BABEL_FLOW` + * - To require the `@flow` directive in source, set it to `{ requireDirective: true }` + */ + enableFlow?: boolean | { requireDirective: boolean }; + + // DEPRECATE: flowRequireDirective?: boolean; + + /** + * Add `@babel/plugin-proposal-decorators` + * - **Default: `false`** + * - To use the legacy decorators behavior, set it to `{ legacy: true }` + * - if not set, then we check env `BABEL_PROPOSAL_DECORATORS` + */ + proposalDecorators?: boolean | { legacy: boolean }; + + // DEPRECATE: legacyDecorators?: boolean; + + /** + * Add `@babel/plugin-proposal-class-properties` for class properties support + * - **Default: `false`** + * - To use loose class props behavior, set it to `{ loose: true }`, which compile to + * assignment expression instead of `Object.defineProperty`. + * - if not set, then we check env `BABEL_CLASS_PROPS` + */ + transformClassProps?: boolean | { loose: boolean }; + + // DEPRECATE: looseClassProps?: boolean; + + // DEPRECATE: + // envTargets?: { + // default?: {}; + // node?: {}; + // }; + + // DEPRECATE: target?: string | string[]; + + // DEPRECATE: extendLoader?: {}; +}; diff --git a/packages/xarc-app-dev/src/config/opt2/karma-options.ts b/packages/xarc-app-dev/src/config/opt2/karma-options.ts new file mode 100644 index 000000000..6c0437f36 --- /dev/null +++ b/packages/xarc-app-dev/src/config/opt2/karma-options.ts @@ -0,0 +1,15 @@ +// configs from ../env-karma.ts + +/** + * configurable options for karma + * - Only applicable if `@xarc/opt-karma` feature is added and enabled. + */ +export type KarmaConfigs = { + /** + * Set the browser to use for karma. + * + * - **Default: "chrome"** + * - if not set, then check env KARMA_BROWSER + */ + browser?: "chrome" | "phantomjs"; +}; diff --git a/packages/xarc-app-dev/src/config/opt2/webpack-options.ts b/packages/xarc-app-dev/src/config/opt2/webpack-options.ts new file mode 100644 index 000000000..8c9cf0c3c --- /dev/null +++ b/packages/xarc-app-dev/src/config/opt2/webpack-options.ts @@ -0,0 +1,141 @@ +// webpack config collected from env-webpack.ts + +/** + * User configurable options that are related to Webpack + */ +export type WebpackOptions = { + /** + * Enable webpack dev mode. **Default: `false`** + * - If not set, then will check env `WEBPACK_DEV` + */ + webpackDev?: boolean; + /** + * Host name to use for webpack dev URL. **Default: `localhost`** + * - If not set, then check env `WEBPACK_HOST`, then `WEBPACK_DEV_HOST`, and finally `HOST` + */ + devHostname?: string; + /** + * Port to use for webpack dev URL. **Default: 2992** + * - If not set, then check env `WEBPACK_DEV_PORT` + */ + devPort?: number; + + /** + * Using a built-in reverse proxy, the webpack dev assets are served from the + * same host and port as the app. In that case, the URLs to assets are relative + * without protocol, host, port. + * + * However, user can simulate CDN server with the proxy and have assets URLs + * specifying different host/port from the app. To do that, the following + * should be defined. + * + * - If not set, then check env `WEBPACK_DEV_CDN_PROTOCOL` + */ + cdnProtocol?: string; + /** + * Host name to use for CDN simulation, see option `cdnProtocol` for more info. + * + * - If not set, then check env `WEBPACK_DEV_CDN_HOST` + */ + cdnHostname?: string; + /** + * Host name to use for CDN simulation, see option `cdnProtocol` for more info. + * + * - If not set, then check env `WEBPACK_DEV_CDN_PORT` + */ + cdnPort?: number; + + /** + * in dev mode, all webpack output are saved to memory only, but some files like + * stats.json are needed by different uses and the stats partial saves a copy to + * disk. It will use this as the path to save the file. + * - **Default: `.etmp`** + * - If not set, then check env `WEBPACK_DEV_ARTIFACTS_PATH` + */ + devArtifactsPath?: string; + + /** + * Set to true to explicitly enable CSS module support for all style files + * - **Default: `undefined` (auto detect)** + * - If not set, then check env `CSS_MODULE_SUPPORT` + */ + cssModuleSupport?: boolean; + + /** + * Enable loading `@babel/polyfill` for application + * - **Default: `false`** + */ + enableBabelPolyfill?: boolean; + + /** + * Enable webpack's NodeSourcePlugin to simulate node.js libs in browser + * - **Default: `false`** + * - If not set, then check env `ENABLE_NODESOURCE_PLUGIN` + * @remarks + * This will bundle 100K+ of JavaScript to simulate node.js env + */ + enableNodeSourcePlugin?: boolean; + + /** + * Support hot module reload for your web app code + * - **Default: `true`** + * - if not set, then we check env `WEBPACK_HOT_MODULE_RELOAD` + */ + enableHotModuleReload?: boolean; + /** + * If hot module reload is enabled, then if you make a change that has error, + * then an overlay on top of the browser page will show the error. + * - **Default: `true`** + * - if not set, then we check env `WEBPACK_DEV_WARNINGS_OVERLAY` + */ + enableWarningsOverlay?: boolean; + + /** + * Size limit to prevent inlining woff fonts data + * - **Default: `1000`** + * - if not set, then we check env `WOFF_FONT_INLINE_LIMIT` + */ + woffFontInlineLimit?: number; + + /** + * https://webpack.js.org/configuration/resolve/#resolve-symlinks + * - **Default: `false`** + * - if not set, then we check env `WEBPACK_PRESERVE_SYMLINKS`, then `NODE_PRESERVE_SYMLINKS` + */ + preserveSymlinks?: boolean; + + /** + * If CSS module is used, then use a shorten class name for CSS in production mode + * - **Default: `true`** + * - if not set, then we check env `ENABLE_SHORTEN_CSS_NAMES` + */ + enableShortenCSSNames?: boolean; + + /** + * Code splitting should optimize to minimize the number of JS chunks are generated. + * + * **Default: `false`** + * - if not set, then we check env: `MINIMIZE_SUBAPP_CHUNKS` + */ + minimizeSubappChunks?: boolean; + + /** + * Custom options for optimizing critical CSS + * - if not set, then we check env: `OPTIMIZE_CSS_OPTIONS` (which should be a JSON string) + * - TBD: define type for it + */ + optimizeCssOptions?: object; + /** + * Custom object with list of webpack DLLs to load + * - **Default: `{}`** + * - if not set, then we check env: `ELECTRODE_LOAD_DLLS` (which should be a JSON string) + * - TBD: define type for it + */ + loadDlls?: object; + /** + * Should webpack minify code output in production mode? + * - **Default: `true`** + * - Useful if you want to build production without minifying for debugging + */ + minify?: boolean; +}; diff --git a/packages/xarc-app-dev/src/config/opt2/xarc-options.ts b/packages/xarc-app-dev/src/config/opt2/xarc-options.ts new file mode 100644 index 000000000..d3fba22a9 --- /dev/null +++ b/packages/xarc-app-dev/src/config/opt2/xarc-options.ts @@ -0,0 +1,80 @@ +import { AddOnFeatures } from "./add-on-features"; +import { WebpackOptions } from "./webpack-options"; +import { BabelOptions } from "./babel-options"; + +export type XarcOptions = { + // configurations from env-app.ts + + /** + * hostname to listen on for serving your application + * + * - **Default: `localhost`** + * - If not set, then check env `HOST` + */ + host?: string; + + /** + * port to listen on for serving your application + * + * @remarks + * This is what the dev proxy listens on. Your app server + * listens on a different port that the proxy forwards to. + * + * - **Default: `3000`** + * - If it's 443, then automatically enable HTTPS. + * - If not set, then check env `PORT` + */ + port?: number; + + /** + * In case you want to serve your app with https on a port other + * than 443, then you can set it with this. + * + * - if not set, then check env `ELECTRODE_DEV_HTTPS`, then `XARC_DEV_HTTPS` + * + * @remarks + * If this is set, it cannot be the same as `port`, which then will be + * forced to be HTTP only. + */ + httpsPort?: number; + /** + * Port number for your app server to listen on so the dev proxy + * can forward request to it. + * + * - **Default: `3100`** + * - If not set, then check env `APP_PORT_FOR_PROXY`, then `APP_SERVER_PORT` + */ + appServerPort?: number; // renamed from portForProxy + + /** + * Dev admin log level + * + * - if not set, then check env `ELECTRODE_ADMIN_LOG_LEVEL`, `DEV_ADMIN_LOG_LEVEL`, then `XARC_ADMIN_LOG_LEVEL` + * - TBD: list levels + */ + adminLogLevel?: number; + + /** + * Port number the dev admin listens to serve its pages. + * + * - **Default: 8991** + * - if not set, then check env `ELECTRODE_ADMIN_PORT` + * + */ + adminPort?: number; + + /** + * Additional optional features to enable + */ + addOnFeatures?: AddOnFeatures; + + /** + * options related to webpack + */ + webpackOptions?: WebpackOptions; + + /** + * options related to babel transpiler + */ + babelOptions?: BabelOptions; +}; diff --git a/packages/xarc-app-dev/src/config/options.ts b/packages/xarc-app-dev/src/config/options.ts index 3308be108..d62392f31 100644 --- a/packages/xarc-app-dev/src/config/options.ts +++ b/packages/xarc-app-dev/src/config/options.ts @@ -5,7 +5,6 @@ const Path = require("path"); const optionalRequire = require("optional-require")(require); const constants = require("./constants"); const utils = require("../lib/utils"); -require("../typedef"); function checkOptArchetypeInAppDep(dependencies, isDev = undefined) { const options = dependencies @@ -47,7 +46,7 @@ function checkOptArchetypeInAppDep(dependencies, isDev = undefined) { const getUserConfigOptions = (packageNames, devPackageNames) => Object.assign( - { reactLib: "react", karma: true, sass: false, options: {} }, + { reactLib: "react", karma: true, sass: false }, optionalRequire(Path.resolve("archetype/config"), { default: {} }).options, // // Check for any optional archetype in application's devDependencies or dependencies diff --git a/packages/xarc-app-dev/src/config/user-config.ts b/packages/xarc-app-dev/src/config/user-config.ts index f31aecb56..6a4e86fd6 100644 --- a/packages/xarc-app-dev/src/config/user-config.ts +++ b/packages/xarc-app-dev/src/config/user-config.ts @@ -6,6 +6,9 @@ const { merge } = require("lodash"); const optionalRequire = require("optional-require")(require); let cachedUserConfig = null; +/** + * Load user's config under the directory archetype/config + */ module.exports = function getUserConfig() { cachedUserConfig = cachedUserConfig || merge({ options: {} }, optionalRequire(Path.resolve("archetype/config"))); diff --git a/packages/xarc-app-dev/src/lib/load-xrun-tasks.ts b/packages/xarc-app-dev/src/lib/load-xrun-tasks.ts index 65e546bd9..ecf8d351f 100644 --- a/packages/xarc-app-dev/src/lib/load-xrun-tasks.ts +++ b/packages/xarc-app-dev/src/lib/load-xrun-tasks.ts @@ -25,8 +25,6 @@ const chalk = require("chalk"); const mkdirp = require("mkdirp"); const xsh = require("xsh"); -require("../typedef"); - /** * Get the webpack's CLI command from @xarc/webpack * diff --git a/packages/xarc-app-dev/src/lib/utils.ts b/packages/xarc-app-dev/src/lib/utils.ts index a840c5a01..6857603fe 100644 --- a/packages/xarc-app-dev/src/lib/utils.ts +++ b/packages/xarc-app-dev/src/lib/utils.ts @@ -5,7 +5,6 @@ import * as pkgUp from "pkg-up"; const Path = require("path"); const Fs = require("fs"); -require("../typedef"); const Url = require("url"); diff --git a/packages/xarc-app-dev/src/typedef.js b/packages/xarc-app-dev/src/typedef.js deleted file mode 100644 index 0341f9cc2..000000000 --- a/packages/xarc-app-dev/src/typedef.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @typedef {object} CreateXarcOptions - * @property {boolean} [enableFeatures=true] - enable archetype features extensions - * @property {string[]} [electrodePackages=[]] - electrode-archetype* package names, opt out of parsing from package.json on disk - * @property {string[]} [electrodePackagesDev=[]] - electrode-archetype* package names, opt out of parsing from package.json on disk - * @property {boolean} [assertNoGulpExecution=true] - assert that legacy gulp command is not executed - * @property {boolean} [assertDevArchetypePresent=true] - assert that sibling development archetype is installed - */ diff --git a/packages/xarc-app/lib/app-mode.js b/packages/xarc-app/lib/app-mode.js index 9a68bb579..ba99fdaa3 100644 --- a/packages/xarc-app/lib/app-mode.js +++ b/packages/xarc-app/lib/app-mode.js @@ -38,7 +38,6 @@ function makeAppMode(prodDir = constants.PROD_DIR, reactLib = "react") { const savedFileFP = Path.resolve(savedFile); const subApps = subappUtil.scanSubAppsFromDir("src"); const hasSubApps = Object.keys(subApps).length > 0; - // // app still has src directory in production mode so we know // app is definitely in the src/lib dir structure setup @@ -107,5 +106,4 @@ function makeAppMode(prodDir = constants.PROD_DIR, reactLib = "react") { version }; } - module.exports = makeAppMode;