From 72a87c5d2cf0e536b81734a7eecaa182cb6fe38c Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Thu, 19 Nov 2020 21:45:16 +0100 Subject: [PATCH] chore: more base configuration --- packages/webpack5/README.md | 30 +++ .../__snapshots__/react.spec.ts.snap | 212 ++++++++++++++---- .../__snapshots__/vue.spec.ts.snap | 122 +++++++--- .../__tests__/configuration/react.spec.ts | 21 +- .../__tests__/configuration/vue.spec.ts | 10 +- packages/webpack5/__tests__/index.spec.ts | 1 + packages/webpack5/jest.config.js | 5 +- packages/webpack5/jest.setup.ts | 20 ++ packages/webpack5/package.json | 53 +++-- packages/webpack5/src/configuration/base.ts | 47 +++- packages/webpack5/src/configuration/react.ts | 4 +- packages/webpack5/src/configuration/vue.ts | 12 +- packages/webpack5/src/helpers/project.ts | 51 +++++ .../webpack5/src/helpers/projectHelpers.ts | 40 ---- packages/webpack5/src/helpers/temp.ts | 10 +- packages/webpack5/src/index.ts | 21 +- .../src/plugins/WatchStateLoggerPlugin.ts | 3 +- packages/webpack5/tsconfig.json | 3 +- 18 files changed, 492 insertions(+), 173 deletions(-) create mode 100644 packages/webpack5/README.md create mode 100644 packages/webpack5/jest.setup.ts create mode 100644 packages/webpack5/src/helpers/project.ts delete mode 100644 packages/webpack5/src/helpers/projectHelpers.ts diff --git a/packages/webpack5/README.md b/packages/webpack5/README.md new file mode 100644 index 000000000..d7c392220 --- /dev/null +++ b/packages/webpack5/README.md @@ -0,0 +1,30 @@ +@nativescript/webpack rewrite + +The rewrite allows us to simplify things, and introduce some breaking changes. +Listing them here, so we can keep track of them - will be in the merge commit, and the release notes once we are ready. + +BREAKING CHANGES: + - `package.json` main should now use a relative path to the package.json instead of the app directory + + For example (given we have a `src` directory where our app is): + + `"main": "app.js"` becomes `"main": "src/app.js"` + + This simplifies things, and will allow ctrl/cmd + clicking on the filename in some editors. + + - `postinstall` scripts have been removed. + + The configuration will not need to change in the user projects between updates. + + For existing projects we will provide an easy upgrade path, through `ns migrate` and a binary in the package. + + For new projects `ns create` should create the config file by invoking a binary in the package. + + - removed resolutions for short imports - use full imports instead. + + For example: + ``` + import http from 'http' + // becomes + import { http } from '@nativescript/core' + ``` diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap index 68f82bc02..b4d996335 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap @@ -3,6 +3,18 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR enabled 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/android/app/src/main/assets/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -12,7 +24,17 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR 'react-dom': 'react-nativescript' }, extensions: [ - '.tsx' + '.tsx', + '.android.ts', + '.ts', + '.android.js', + '.js', + '.android.css', + '.css', + '.android.scss', + '.scss', + '.android.json', + '.json' ] }, resolveLoader: { @@ -96,6 +118,22 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: false, + sequences: false + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( @@ -113,17 +151,12 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR 'global.isAndroid': true, 'global.isIOS': false, process: 'global.process', + profile: '() => {}', __DEV__: 'true', __TEST__: 'false', 'process.env.NODE_ENV': '\\"development\\"' } ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] - } - ), /* config.plugin('WatchStateLoggerPlugin') */ new WatchStateLoggerPlugin(), /* config.plugin('ReactRefreshWebpackPlugin') */ @@ -136,7 +169,7 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR ], entry: { bundle: [ - 'todo/main' + 'src/app.js' ] } }" @@ -145,6 +178,18 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR exports[`react configuration > android > base config 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/android/app/src/main/assets/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -154,7 +199,17 @@ exports[`react configuration > android > base config 1`] = ` 'react-dom': 'react-nativescript' }, extensions: [ - '.tsx' + '.tsx', + '.android.ts', + '.ts', + '.android.js', + '.js', + '.android.css', + '.css', + '.android.scss', + '.scss', + '.android.json', + '.json' ] }, resolveLoader: { @@ -238,6 +293,22 @@ exports[`react configuration > android > base config 1`] = ` } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: false, + sequences: false + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( @@ -255,23 +326,18 @@ exports[`react configuration > android > base config 1`] = ` 'global.isAndroid': true, 'global.isIOS': false, process: 'global.process', + profile: '() => {}', __DEV__: 'true', __TEST__: 'false', 'process.env.NODE_ENV': '\\"development\\"' } ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] - } - ), /* config.plugin('WatchStateLoggerPlugin') */ new WatchStateLoggerPlugin() ], entry: { bundle: [ - 'todo/main' + 'src/app.js' ] } }" @@ -280,6 +346,18 @@ exports[`react configuration > android > base config 1`] = ` exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR enabled 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/ios/__jest__/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -289,7 +367,17 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena 'react-dom': 'react-nativescript' }, extensions: [ - '.tsx' + '.tsx', + '.ios.ts', + '.ts', + '.ios.js', + '.js', + '.ios.css', + '.css', + '.ios.scss', + '.scss', + '.ios.json', + '.json' ] }, resolveLoader: { @@ -373,12 +461,28 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: true, + sequences: true + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( { cleanOnceBeforeBuildPatterns: [ - 'platforms/ios/[todo]/app/**/*' + 'platforms/ios/__jest__/app/**/*' ], verbose: true } @@ -390,17 +494,12 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena 'global.isAndroid': false, 'global.isIOS': true, process: 'global.process', + profile: '() => {}', __DEV__: 'true', __TEST__: 'false', 'process.env.NODE_ENV': '\\"development\\"' } ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] - } - ), /* config.plugin('WatchStateLoggerPlugin') */ new WatchStateLoggerPlugin(), /* config.plugin('ReactRefreshWebpackPlugin') */ @@ -412,11 +511,11 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena ) ], entry: { - inspector_modules: [ - 'tns_modules/@nativescript/core/inspector_modules' - ], bundle: [ - 'todo/main' + 'src/app.js' + ], + 'tns_modules/@nativescript/core/inspector_modules': [ + '@nativescript/core/inspector_modules' ] } }" @@ -425,6 +524,18 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena exports[`react configuration > ios > base config 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/ios/__jest__/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -434,7 +545,17 @@ exports[`react configuration > ios > base config 1`] = ` 'react-dom': 'react-nativescript' }, extensions: [ - '.tsx' + '.tsx', + '.ios.ts', + '.ts', + '.ios.js', + '.js', + '.ios.css', + '.css', + '.ios.scss', + '.scss', + '.ios.json', + '.json' ] }, resolveLoader: { @@ -518,12 +639,28 @@ exports[`react configuration > ios > base config 1`] = ` } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: true, + sequences: true + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( { cleanOnceBeforeBuildPatterns: [ - 'platforms/ios/[todo]/app/**/*' + 'platforms/ios/__jest__/app/**/*' ], verbose: true } @@ -535,26 +672,21 @@ exports[`react configuration > ios > base config 1`] = ` 'global.isAndroid': false, 'global.isIOS': true, process: 'global.process', + profile: '() => {}', __DEV__: 'true', __TEST__: 'false', 'process.env.NODE_ENV': '\\"development\\"' } ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] - } - ), /* config.plugin('WatchStateLoggerPlugin') */ new WatchStateLoggerPlugin() ], entry: { - inspector_modules: [ - 'tns_modules/@nativescript/core/inspector_modules' - ], bundle: [ - 'todo/main' + 'src/app.js' + ], + 'tns_modules/@nativescript/core/inspector_modules': [ + '@nativescript/core/inspector_modules' ] } }" diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap index 93d95bd7f..1847fe11a 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap @@ -3,6 +3,18 @@ exports[`vue configuration for android 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/android/app/src/main/assets/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -12,7 +24,17 @@ exports[`vue configuration for android 1`] = ` vue: 'nativescript-vue' }, extensions: [ - '.vue' + '.vue', + '.android.ts', + '.ts', + '.android.js', + '.js', + '.android.css', + '.css', + '.android.scss', + '.scss', + '.android.json', + '.json' ] }, resolveLoader: { @@ -100,7 +122,25 @@ exports[`vue configuration for android 1`] = ` } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: false, + sequences: false + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ + /* config.plugin('VueLoaderPlugin') */ + new VueLoaderPlugin(), /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( { @@ -116,23 +156,16 @@ exports[`vue configuration for android 1`] = ` 'global.NS_WEBPACK': true, 'global.isAndroid': true, 'global.isIOS': false, - process: 'global.process' - } - ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] + process: 'global.process', + profile: '() => {}' } ), /* config.plugin('WatchStateLoggerPlugin') */ - new WatchStateLoggerPlugin(), - /* config.plugin('VueLoaderPlugin') */ - new VueLoaderPlugin() + new WatchStateLoggerPlugin() ], entry: { bundle: [ - 'todo/main' + 'src/app.js' ] } }" @@ -141,6 +174,18 @@ exports[`vue configuration for android 1`] = ` exports[`vue configuration for ios 1`] = ` "{ mode: 'development', + externals: [ + 'package.json' + ], + devtool: 'inline-source-map', + target: 'node', + output: { + path: '__jest__/platforms/ios/__jest__/app', + pathinfo: false, + publicPath: '', + libraryTarget: 'commonjs', + globalObject: 'global' + }, resolve: { symlinks: true, alias: { @@ -150,7 +195,17 @@ exports[`vue configuration for ios 1`] = ` vue: 'nativescript-vue' }, extensions: [ - '.vue' + '.vue', + '.ios.ts', + '.ts', + '.ios.js', + '.js', + '.ios.css', + '.css', + '.ios.scss', + '.scss', + '.ios.json', + '.json' ] }, resolveLoader: { @@ -238,12 +293,30 @@ exports[`vue configuration for ios 1`] = ` } ] }, + optimization: { + minimizer: [ + /* config.optimization.minimizer('TerserPlugin') */ + new TerserPlugin( + { + terserOptions: { + compress: { + collapse_vars: true, + sequences: true + }, + keep_fnames: true + } + } + ) + ] + }, plugins: [ + /* config.plugin('VueLoaderPlugin') */ + new VueLoaderPlugin(), /* config.plugin('CleanWebpackPlugin') */ new CleanWebpackPlugin( { cleanOnceBeforeBuildPatterns: [ - 'platforms/ios/[todo]/app/**/*' + 'platforms/ios/__jest__/app/**/*' ], verbose: true } @@ -254,26 +327,19 @@ exports[`vue configuration for ios 1`] = ` 'global.NS_WEBPACK': true, 'global.isAndroid': false, 'global.isIOS': true, - process: 'global.process' - } - ), - /* config.plugin('CopyWebpackPlugin') */ - new CopyPluginTemp( - { - patterns: [] + process: 'global.process', + profile: '() => {}' } ), /* config.plugin('WatchStateLoggerPlugin') */ - new WatchStateLoggerPlugin(), - /* config.plugin('VueLoaderPlugin') */ - new VueLoaderPlugin() + new WatchStateLoggerPlugin() ], entry: { - inspector_modules: [ - 'tns_modules/@nativescript/core/inspector_modules' - ], bundle: [ - 'todo/main' + 'src/app.js' + ], + 'tns_modules/@nativescript/core/inspector_modules': [ + '@nativescript/core/inspector_modules' ] } }" diff --git a/packages/webpack5/__tests__/configuration/react.spec.ts b/packages/webpack5/__tests__/configuration/react.spec.ts index 41f2b17dd..ca3e413fe 100644 --- a/packages/webpack5/__tests__/configuration/react.spec.ts +++ b/packages/webpack5/__tests__/configuration/react.spec.ts @@ -1,6 +1,7 @@ // @ts-ignore import Config from 'webpack-chain'; import react from '../../src/configuration/react'; +import { init } from '../../src'; describe('react configuration', () => { const platforms = ['ios', 'android']; @@ -8,20 +9,18 @@ describe('react configuration', () => { for (let platform of platforms) { describe(`> ${platform} >`, () => { it(`base config`, () => { - expect( - react(new Config(), { - [platform]: true, - }).toString() - ).toMatchSnapshot(); + init({ + [platform]: true, + }); + expect(react(new Config()).toString()).toMatchSnapshot(); }); it(`adds ReactRefreshWebpackPlugin when HMR enabled`, () => { - expect( - react(new Config(), { - [platform]: true, - hmr: true, - }).toString() - ).toMatchSnapshot(); + init({ + [platform]: true, + hmr: true, + }); + expect(react(new Config()).toString()).toMatchSnapshot(); }); }); } diff --git a/packages/webpack5/__tests__/configuration/vue.spec.ts b/packages/webpack5/__tests__/configuration/vue.spec.ts index 4236882c9..f43ef9964 100644 --- a/packages/webpack5/__tests__/configuration/vue.spec.ts +++ b/packages/webpack5/__tests__/configuration/vue.spec.ts @@ -1,17 +1,17 @@ // @ts-ignore import Config from 'webpack-chain'; import vue from '../../src/configuration/vue'; +import { init } from '../../src'; describe.only('vue configuration', () => { const platforms = ['ios', 'android']; for (let platform of platforms) { it(`for ${platform}`, () => { - expect( - vue(new Config(), { - [platform]: true, - }).toString() - ).toMatchSnapshot(); + init({ + [platform]: true, + }); + expect(vue(new Config()).toString()).toMatchSnapshot(); }); } }); diff --git a/packages/webpack5/__tests__/index.spec.ts b/packages/webpack5/__tests__/index.spec.ts index c4c08cc39..500931f4d 100644 --- a/packages/webpack5/__tests__/index.spec.ts +++ b/packages/webpack5/__tests__/index.spec.ts @@ -13,6 +13,7 @@ describe('@nativescript/webpack', () => { }); it('applies chain configs', () => { + webpack.useConfig(false); expect(webpack.chainWebpack).toBeInstanceOf(Function); const chainFn = jest.fn(); diff --git a/packages/webpack5/jest.config.js b/packages/webpack5/jest.config.js index fff85eb81..0febf167d 100644 --- a/packages/webpack5/jest.config.js +++ b/packages/webpack5/jest.config.js @@ -3,5 +3,8 @@ module.exports = { testEnvironment: 'node', moduleNameMapper: { '^@nativescript/webpack$': '/src' - } + }, + setupFiles: [ + '/jest.setup.ts' + ] }; diff --git a/packages/webpack5/jest.setup.ts b/packages/webpack5/jest.setup.ts new file mode 100644 index 000000000..67a009407 --- /dev/null +++ b/packages/webpack5/jest.setup.ts @@ -0,0 +1,20 @@ +// we are mocking the cwd for the tests, since webpack needs absolute paths +// and we don't want them in tests +process.cwd = () => '__jest__'; + +// a virtual mock for package.json +jest.mock( + '__jest__/package.json', + () => ({ + main: 'src/app.js', + }), + { virtual: true } +); + +jest.mock('path', () => ({ + ...jest.requireActual('path'), + // we are mocking resolve to just simply join the paths for tests + resolve(...args) { + return args.join('/'); + }, +})); diff --git a/packages/webpack5/package.json b/packages/webpack5/package.json index c8413ada4..1ebe767ec 100644 --- a/packages/webpack5/package.json +++ b/packages/webpack5/package.json @@ -1,28 +1,37 @@ { - "name": "@nativescript/webpack", - "version": "4.0.0-dev", - "private": true, - "main": "dist/index.js", + "name": "@nativescript/webpack", + "version": "4.0.0-dev", + "private": true, + "main": "dist/index.js", "files": [ "dist" ], - "license": "Apache-2.0", - "scripts": { - "build": "tsc", - "test": "jest", + "license": "Apache-2.0", + "scripts": { + "build": "tsc", + "test": "jest", "prepack": "npm run build && cp -R src/stubs dist/stubs" - }, - "devDependencies": { - "@types/jest": "^26.0.15", - "jest": "^26.6.3", - "ts-jest": "^26.4.4", - "typescript": "^4.0.5", - "webpack": "^5.4.0" - }, - "dependencies": { - "clean-webpack-plugin": "^3.0.0", - "vue-loader": "^15.9.5", - "webpack-chain": "^6.5.1", - "webpack-merge": "^5.4.0" - } + }, + "dependencies": { + "@babel/core": "^7.12.3", + "babel-loader": "^8.2.1", + "clean-webpack-plugin": "^3.0.0", + "cli-highlight": "^2.1.4", + "terser-webpack-plugin": "^5.0.3", + "vue-loader": "^15.9.5", + "webpack": "^5.4.0", + "webpack-chain": "^6.5.1", + "webpack-cli": "^4.2.0", + "webpack-merge": "^5.4.0", + "worker-plugin": "^5.0.0" + }, + "devDependencies": { + "@types/jest": "^26.0.15", + "jest": "^26.6.3", + "ts-jest": "^26.4.4", + "typescript": "^4.0.5" + }, + "peerDependencies": { + "nativescript-vue-template-compiler": "^2.8.1" + } } diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index c25c4c31d..26ed7b485 100644 --- a/packages/webpack5/src/configuration/base.ts +++ b/packages/webpack5/src/configuration/base.ts @@ -1,20 +1,46 @@ import Config from 'webpack-chain'; import { IWebpackEnv, Platform } from '../index'; +import { getAbsoluteDistPath, getDistPath, getEntryPath, getPackageJson } from '../helpers/project'; + import { CleanWebpackPlugin } from 'clean-webpack-plugin'; -import { getDistPath } from '../helpers/projectHelpers'; import { DefinePlugin } from 'webpack'; import { WatchStateLoggerPlugin } from '../plugins/WatchStateLoggerPlugin'; +import TerserPlugin from 'terser-webpack-plugin'; export default function (config: Config, env: IWebpackEnv): Config { - const distPath = getDistPath(env); + const entryPath = getEntryPath(); + const distPath = getDistPath(); const platform = determinePlatformFromEnv(env); + const packageJson = getPackageJson(); const mode = env.production ? 'production' : 'development'; // set mode config.mode(mode); + config.externals(['package.json']); + // todo: devtool - // config.devtool() + config.devtool('inline-source-map'); + + config.target('node'); + + config.entry('bundle').add(entryPath); + + config.output.path(getAbsoluteDistPath()).pathinfo(false).publicPath('').libraryTarget('commonjs').globalObject('global'); + + // Set up Terser options + config.optimization.minimizer('TerserPlugin').use(TerserPlugin, [ + { + terserOptions: { + compress: { + collapse_vars: platform !== 'android', + sequences: platform !== 'android', + }, + // todo: move into vue.ts if not required in other flavors? + keep_fnames: true, + }, + }, + ]); // look for loaders in // - @nativescript/webpack/loaders @@ -23,10 +49,10 @@ export default function (config: Config, env: IWebpackEnv): Config { // inspector_modules config.when(shouldIncludeInspectorModules(env), (config) => { - config.entry('inspector_modules').add('tns_modules/@nativescript/core/inspector_modules').end(); + config.entry('tns_modules/@nativescript/core/inspector_modules').add('@nativescript/core/inspector_modules'); }); - config.entry('bundle').add('todo/main').end(); + config.resolve.extensions.add(`.${platform}.ts`).add('.ts').add(`.${platform}.js`).add('.js').add(`.${platform}.css`).add('.css').add(`.${platform}.scss`).add('.scss').add(`.${platform}.json`).add('.json'); // base aliases config.resolve.alias.set('~/package.json', 'package.json').set('~', 'appFullPath').set('@', 'appFullPath'); @@ -96,17 +122,18 @@ export default function (config: Config, env: IWebpackEnv): Config { 'global.isAndroid': platform === 'android', 'global.isIOS': platform === 'ios', process: 'global.process', + profile: '() => {}', }, ]); // todo: we should probably move away from CopyWebpackPlugin // it has many issues we can solve by simply copying files **before** the build even starts // this is just a temp inline plugin that does nothing while building out the configs. - config.plugin('CopyWebpackPlugin').use(function CopyPluginTemp() {}, [ - { - patterns: [], - }, - ]); + // config.plugin('CopyWebpackPlugin').use(function CopyPluginTemp() {}, [ + // { + // patterns: [], + // }, + // ]); // add the WatchStateLogger plugin used to notify the CLI of build state config.plugin('WatchStateLoggerPlugin').use(WatchStateLoggerPlugin); diff --git a/packages/webpack5/src/configuration/react.ts b/packages/webpack5/src/configuration/react.ts index a6ea5f427..ac44c3934 100644 --- a/packages/webpack5/src/configuration/react.ts +++ b/packages/webpack5/src/configuration/react.ts @@ -1,9 +1,9 @@ import base from './base'; -import { IWebpackEnv } from '@nativescript/webpack'; +import { env as _env, IWebpackEnv } from '@nativescript/webpack'; import Config from 'webpack-chain'; import { merge } from 'webpack-merge'; -export default function (config: Config, env: IWebpackEnv): Config { +export default function (config: Config, env: IWebpackEnv = _env): Config { base(config, env); // todo: use env diff --git a/packages/webpack5/src/configuration/vue.ts b/packages/webpack5/src/configuration/vue.ts index ff4c474e8..a2134ac03 100644 --- a/packages/webpack5/src/configuration/vue.ts +++ b/packages/webpack5/src/configuration/vue.ts @@ -1,10 +1,10 @@ import base from './base'; import Config from 'webpack-chain'; import { VueLoaderPlugin } from 'vue-loader'; -import { IWebpackEnv } from '../index'; +import { env as _env, IWebpackEnv } from '../index'; import { merge } from 'webpack-merge'; -export default function (config: Config, env: IWebpackEnv): Config { +export default function (config: Config, env: IWebpackEnv = _env): Config { base(config, env); // resolve .vue files @@ -35,8 +35,12 @@ export default function (config: Config, env: IWebpackEnv): Config { }); }); - // add VueLoaderPlugin - config.plugin('VueLoaderPlugin').use(VueLoaderPlugin); + // add VueLoaderPlugin as the first plugin + config + .plugin('VueLoaderPlugin') + // @ts-ignore + .before(config.plugins.values()[0].name) + .use(VueLoaderPlugin); // add an alias for vue, since some plugins may try to import it config.resolve.alias.set('vue', 'nativescript-vue'); diff --git a/packages/webpack5/src/helpers/project.ts b/packages/webpack5/src/helpers/project.ts new file mode 100644 index 000000000..5f471acf1 --- /dev/null +++ b/packages/webpack5/src/helpers/project.ts @@ -0,0 +1,51 @@ +import { env } from '../index'; +import { resolve, basename } from 'path'; + +export function getProjectRootPath(): string { + // todo: find actual path? + + return process.cwd(); + //__dirname +} + +export function getAbsoluteDistPath() { + return resolve(getProjectRootPath(), getDistPath()); +} + +export function getEntryPath() { + const packageJson = getPackageJson(); + + return resolve(packageJson.main); +} + +export function getDistPath() { + if (env.ios) { + const appName = basename(getProjectRootPath()); + return `platforms/ios/${appName}/app`; + } + + if (env.android) { + return `platforms/android/app/src/main/assets/app`; + } + + // todo: additional platforms + // perhaps we could combine platform specifics into "plugins" + // 3rd party platforms would be treated the same +} + +interface IPackageJson { + main?: string; + dependencies?: { + [name: string]: string; + }; + devDependencies?: { + [name: string]: string; + }; + // todo: add additional fields as we require them +} + +export function getPackageJson() { + const packageJsonPath = resolve(getProjectRootPath(), 'package.json'); + + return require(packageJsonPath) as IPackageJson; +} diff --git a/packages/webpack5/src/helpers/projectHelpers.ts b/packages/webpack5/src/helpers/projectHelpers.ts deleted file mode 100644 index 1b1ca17ba..000000000 --- a/packages/webpack5/src/helpers/projectHelpers.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { existsSync } from 'fs'; -import { resolve } from 'path'; -import { IWebpackEnv } from '@nativescript/webpack'; - -export function getDistPath(env: IWebpackEnv) { - if (env.ios) { - return `platforms/ios/[todo]/app`; - } - - if (env.android) { - return `platforms/android/app/src/main/assets/app`; - } - - // todo: additional platforms - // perhaps we could combine platform specifics into "plugins" - // 3rd party platforms would be treated the same -} - -export function getPackageJson(projectDir: string) { - const packageJsonPath = getPackageJsonPath(projectDir); - const result = readJsonFile(packageJsonPath); - - return result; -} - -export function readJsonFile(filePath: string) { - return require(filePath) as { - main: string; - // to be extended? - }; -} - -export function getPackageJsonPath(projectDir: string) { - const packagePath = resolve(projectDir, 'package.json'); - if (existsSync(packagePath)) { - return packagePath; - } else { - return getPackageJsonPath(resolve(projectDir, '..')); - } -} diff --git a/packages/webpack5/src/helpers/temp.ts b/packages/webpack5/src/helpers/temp.ts index c3bae972e..50e422bcd 100644 --- a/packages/webpack5/src/helpers/temp.ts +++ b/packages/webpack5/src/helpers/temp.ts @@ -1,5 +1,5 @@ import { existsSync } from 'fs'; -import { getPackageJson } from './projectHelpers'; +import { getPackageJson } from './project'; import { resolve } from 'path'; // todo: get rid of these or reduce them to their simplest form @@ -20,12 +20,12 @@ function verifyEntryModuleDirectory(appDirectory: string) { } } -function getPackageJsonEntry(appDirectory) { - const packageJsonSource = getPackageJson(appDirectory); +function getPackageJsonEntry() { + const packageJsonSource = getPackageJson(); const entry = packageJsonSource.main; if (!entry) { - throw new Error(`${appDirectory}/package.json must contain a 'main' attribute!`); + throw new Error(`package.json must contain a 'main' attribute!`); } return entry.replace(/\.js$/i, ''); @@ -34,7 +34,7 @@ function getPackageJsonEntry(appDirectory) { export function getEntryModule(appDirectory: string, platform: 'android' | 'ios') { verifyEntryModuleDirectory(appDirectory); - const entry = getPackageJsonEntry(appDirectory); + const entry = getPackageJsonEntry(); const tsEntryPath = resolve(appDirectory, `${entry}.ts`); const jsEntryPath = resolve(appDirectory, `${entry}.js`); diff --git a/packages/webpack5/src/index.ts b/packages/webpack5/src/index.ts index 266809741..20b30ba64 100644 --- a/packages/webpack5/src/index.ts +++ b/packages/webpack5/src/index.ts @@ -2,6 +2,7 @@ import Config from 'webpack-chain'; import webpack from 'webpack'; import { configs } from './configuration'; import { determineProjectFlavor } from './helpers/flavor'; +import { highlight } from 'cli-highlight'; export type Platform = 'android' | 'ios' | string; @@ -17,14 +18,21 @@ export interface IWebpackEnv { production?: boolean; report?: boolean; hmr?: boolean; + + // enable verbose output + verbose?: boolean; // todo: add others } let webpackChains: any[] = []; let webpackMerges: any[] = []; -let env: IWebpackEnv = {}; let explicitUseConfig = false; +/** + * @internal + */ +export let env: IWebpackEnv = {}; + ////// PUBLIC API export const defaultConfigs = configs; @@ -34,9 +42,11 @@ export function init(_env: IWebpackEnv) { } } -export function useConfig(config: keyof typeof defaultConfigs) { +export function useConfig(config: keyof typeof defaultConfigs | false) { explicitUseConfig = true; - webpackChains.push(configs[config]); + if (config) { + webpackChains.unshift(configs[config]); + } } export function chainWebpack(chainFn: (config: Config, env: IWebpackEnv) => any) { @@ -59,6 +69,11 @@ export function resolveChainableConfig() { return chainFn(config, env); }); + if (env.verbose) { + console.log('Resolved chainable config:'); + console.log(highlight(config.toString(), { language: 'js' })); + } + return config; } diff --git a/packages/webpack5/src/plugins/WatchStateLoggerPlugin.ts b/packages/webpack5/src/plugins/WatchStateLoggerPlugin.ts index 51cd2cdb2..0071cc315 100644 --- a/packages/webpack5/src/plugins/WatchStateLoggerPlugin.ts +++ b/packages/webpack5/src/plugins/WatchStateLoggerPlugin.ts @@ -31,9 +31,10 @@ export class WatchStateLoggerPlugin { } const emittedFiles = Object.keys(compilation.assets).filter((assetKey) => compilation.assets[assetKey].emitted); - const chunkFiles = getChunkFiles(compilation); + process.send && process.send(messages.compilationComplete, (error) => null); + // Send emitted files so they can be LiveSynced if need be process.send && process.send({ emittedFiles, chunkFiles, hash: compilation.hash }, (error) => null); }); diff --git a/packages/webpack5/tsconfig.json b/packages/webpack5/tsconfig.json index ffd723f2e..f56902cf4 100644 --- a/packages/webpack5/tsconfig.json +++ b/packages/webpack5/tsconfig.json @@ -17,7 +17,8 @@ "@nativescript/webpack": ["src"] }, "esModuleInterop": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "stripInternal": true }, "include": ["src"], "exclude": ["node_modules"]