@umijs/preset-umi 4.0.0-beta.14 → 4.0.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/commands/build.js +1 -1
  2. package/dist/commands/dev/createRouteMiddleware.js +2 -2
  3. package/dist/commands/dev/dev.js +9 -3
  4. package/dist/commands/dev/getMarkupArgs.js +2 -1
  5. package/dist/commands/generate/prettier.js +35 -3
  6. package/dist/commands/plugin.js +0 -1
  7. package/dist/features/appData/appData.js +27 -0
  8. package/dist/features/configPlugins/configPlugins.js +5 -2
  9. package/dist/features/configPlugins/schema.js +14 -4
  10. package/dist/features/esmi/Service.js +1 -0
  11. package/dist/features/esmi/esbuildPlugins/requireToImport.d.ts +8 -0
  12. package/dist/features/esmi/esbuildPlugins/requireToImport.js +61 -0
  13. package/dist/features/esmi/esbuildPlugins/topLevelExternal.d.ts +13 -0
  14. package/dist/features/esmi/esbuildPlugins/topLevelExternal.js +37 -0
  15. package/dist/features/esmi/esmi.js +60 -15
  16. package/dist/features/lowImport/babelPlugin.js +18 -5
  17. package/dist/features/mock/getMockData.d.ts +4 -0
  18. package/dist/features/mock/getMockData.js +14 -5
  19. package/dist/features/mock/mock.js +9 -2
  20. package/dist/features/polyfill/polyfill.js +51 -9
  21. package/dist/features/polyfill/publicPathPolyfill.d.ts +3 -0
  22. package/dist/features/polyfill/publicPathPolyfill.js +14 -0
  23. package/dist/features/tmpFiles/routes.js +38 -3
  24. package/dist/features/tmpFiles/tmpFiles.js +99 -7
  25. package/dist/features/vite/vite.d.ts +3 -0
  26. package/dist/features/vite/vite.js +34 -0
  27. package/dist/index.js +2 -0
  28. package/dist/libs/scan.d.ts +5 -1
  29. package/dist/libs/scan.js +58 -4
  30. package/dist/registerMethods.js +2 -2
  31. package/dist/types.d.ts +3 -0
  32. package/dist/utils/transformIEAR.js +7 -2
  33. package/package.json +13 -12
  34. package/templates/history.tpl +15 -0
  35. package/templates/umi.tpl +4 -0
@@ -110,7 +110,7 @@ umi build --clean
110
110
  const { vite } = api.args;
111
111
  const markupArgs = yield (0, getMarkupArgs_1.getMarkupArgs)({ api });
112
112
  // @ts-ignore
113
- const markup = yield (0, server_1.getMarkup)(Object.assign(Object.assign({}, markupArgs), { scripts: ['/umi.js'].concat(markupArgs.scripts), esmScript: !!opts.config.esm || vite, path: '/' }));
113
+ const markup = yield (0, server_1.getMarkup)(Object.assign(Object.assign({}, markupArgs), { styles: ['/umi.css'].concat(markupArgs.styles), scripts: ['/umi.js'].concat(markupArgs.scripts), esmScript: !!opts.config.esm || vite, path: '/' }));
114
114
  (0, fs_1.writeFileSync)((0, path_1.join)(api.paths.absOutputPath, 'index.html'), markup, 'utf-8');
115
115
  utils_1.logger.event('build index.html');
116
116
  // print size
@@ -28,11 +28,11 @@ function createRouteMiddleware(opts) {
28
28
  // add noshim attr for skip importmap shim logic for this modules
29
29
  { content: viteRefreshScript, noshim: '' },
30
30
  { src: '/@vite/client', noshim: '' },
31
- '/.umi/umi.ts',
31
+ opts.api.appData.hasSrcDir ? '/src/.umi/umi.ts' : '/.umi/umi.ts',
32
32
  ];
33
33
  const markupArgs = yield (0, getMarkupArgs_1.getMarkupArgs)(opts);
34
34
  // @ts-ignore
35
- const requestHandler = yield (0, server_1.createRequestHandler)(Object.assign(Object.assign({}, markupArgs), { scripts: (vite ? viteScripts : ['/umi.js']).concat(markupArgs.scripts), esmScript: vite }));
35
+ const requestHandler = yield (0, server_1.createRequestHandler)(Object.assign(Object.assign({}, markupArgs), { styles: ['/umi.css'].concat(markupArgs.styles), scripts: (vite ? viteScripts : ['/umi.js']).concat(markupArgs.scripts), esmScript: vite }));
36
36
  requestHandler(req, res, next);
37
37
  });
38
38
  }
@@ -56,7 +56,7 @@ umi dev
56
56
  PORT=8888 umi dev
57
57
  `,
58
58
  fn() {
59
- var _a;
59
+ var _a, _b;
60
60
  return __awaiter(this, void 0, void 0, function* () {
61
61
  const enableVite = api.args.vite;
62
62
  // clear tmp except cache
@@ -102,9 +102,10 @@ PORT=8888 umi dev
102
102
  key: 'addTmpGenerateWatcherPaths',
103
103
  initialValue: [
104
104
  absPagesPath,
105
+ !api.config.routes && ((_a = api.config.conventionRoutes) === null || _a === void 0 ? void 0 : _a.base),
105
106
  (0, path_1.join)(absSrcPath, 'layouts'),
106
107
  ...(0, watch_1.expandJSPaths)((0, path_1.join)(absSrcPath, 'app')),
107
- ],
108
+ ].filter(Boolean),
108
109
  });
109
110
  utils_1.lodash.uniq(watcherPaths.map(utils_1.winPath)).forEach((p) => {
110
111
  (0, watch_1.watch)({
@@ -238,7 +239,12 @@ PORT=8888 umi dev
238
239
  key: 'onDevCompileDone',
239
240
  args: opts,
240
241
  });
241
- }, mfsuWithESBuild: (_a = api.config.mfsu) === null || _a === void 0 ? void 0 : _a.esbuild });
242
+ }, mfsuWithESBuild: (_b = api.config.mfsu) === null || _b === void 0 ? void 0 : _b.esbuild, cache: {
243
+ buildDependencies: [
244
+ api.pkgPath,
245
+ api.service.configManager.mainConfigFile || '',
246
+ ].filter(Boolean),
247
+ } });
242
248
  if (enableVite) {
243
249
  yield bundlerVite.dev(opts);
244
250
  }
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getMarkupArgs = void 0;
16
16
  const cheerio_1 = __importDefault(require("@umijs/utils/compiled/cheerio"));
17
17
  function getMarkupArgs(opts) {
18
+ var _a;
18
19
  return __awaiter(this, void 0, void 0, function* () {
19
20
  const headScripts = yield opts.api.applyPlugins({
20
21
  key: 'addHTMLHeadScripts',
@@ -42,7 +43,7 @@ function getMarkupArgs(opts) {
42
43
  });
43
44
  return {
44
45
  mountElementId: opts.api.config.mountElementId,
45
- base: opts.api.config.base,
46
+ base: ((_a = opts.api.config.history) === null || _a === void 0 ? void 0 : _a.type) === 'browser' ? opts.api.config.base : '/',
46
47
  routes: opts.api.appData.routes,
47
48
  favicon,
48
49
  headScripts,
@@ -8,8 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  const core_1 = require("@umijs/core");
16
+ const utils_1 = require("@umijs/utils");
17
+ const assert_1 = __importDefault(require("assert"));
13
18
  const fs_1 = require("fs");
14
19
  const path_1 = require("path");
15
20
  exports.default = (api) => {
@@ -23,9 +28,36 @@ exports.default = (api) => {
23
28
  // 存在 .prettierrc,不开启
24
29
  return !(0, fs_1.existsSync)((0, path_1.join)(api.paths.cwd, '.prettierrc'));
25
30
  },
26
- fn: (options) => __awaiter(void 0, void 0, void 0, function* () {
27
- // TODO
28
- console.log(options);
31
+ fn: () => __awaiter(void 0, void 0, void 0, function* () {
32
+ // 1、修改 package.json,加上 prettier 和插件
33
+ api.pkg.devDependencies = Object.assign(Object.assign({}, api.pkg.devDependencies), { prettier: '^2', 'prettier-plugin-organize-imports': '^2', 'prettier-plugin-packagejson': '^2' });
34
+ (0, fs_1.writeFileSync)(api.pkgPath, JSON.stringify(api.pkg, null, 2));
35
+ utils_1.logger.info('Write package.json');
36
+ // 2、添加 .prettierrc 和 .prettierignore
37
+ (0, fs_1.writeFileSync)((0, path_1.join)(api.cwd, '.prettierrc'), `
38
+ {
39
+ "printWidth": 80,
40
+ "singleQuote": true,
41
+ "trailingComma": "all",
42
+ "proseWrap": "never",
43
+ "overrides": [{ "files": ".prettierrc", "options": { "parser": "json" } }],
44
+ "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"]
45
+ }
46
+ `.trimLeft());
47
+ utils_1.logger.info('Write .prettierrc');
48
+ (0, fs_1.writeFileSync)((0, path_1.join)(api.cwd, '.prettierignore'), `
49
+ node_modules
50
+ .umi
51
+ .umi-production
52
+ `.trimLeft());
53
+ utils_1.logger.info('Write .prettierignore');
54
+ // 3、执行 npm install
55
+ const npmClient = api.userConfig.npmClient;
56
+ (0, assert_1.default)(npmClient, `npmClient is required in your config.`);
57
+ (0, utils_1.installWithNpmClient)({
58
+ npmClient,
59
+ });
60
+ utils_1.logger.info(`Install dependencies with ${npmClient}`);
29
61
  }),
30
62
  });
31
63
  };
@@ -7,7 +7,6 @@ exports.default = (api) => {
7
7
  description: 'inspect umi plugins',
8
8
  fn({ args }) {
9
9
  const command = args._[0];
10
- console.log('command', command);
11
10
  if (!command) {
12
11
  throw new Error(`
13
12
  Sub command not found: umi plugin
@@ -14,6 +14,7 @@ const utils_1 = require("@umijs/utils");
14
14
  const fs_1 = require("fs");
15
15
  const path_1 = require("path");
16
16
  const watch_1 = require("../../commands/dev/watch");
17
+ const scan_1 = require("../../libs/scan");
17
18
  const routes_1 = require("../tmpFiles/routes");
18
19
  exports.default = (api) => {
19
20
  api.modifyAppData((memo) => __awaiter(void 0, void 0, void 0, function* () {
@@ -29,6 +30,7 @@ exports.default = (api) => {
29
30
  version: require((0, path_1.join)(api.config.alias.react, 'package.json')).version,
30
31
  };
31
32
  memo.appJS = yield getAppJsInfo();
33
+ memo.vite = api.args.vite ? {} : undefined;
32
34
  return memo;
33
35
  }));
34
36
  // Execute earliest, so that other onGenerateFiles can get it
@@ -43,6 +45,31 @@ exports.default = (api) => {
43
45
  },
44
46
  stage: Number.NEGATIVE_INFINITY,
45
47
  });
48
+ // used in esmi and vite
49
+ api.register({
50
+ key: 'updateAppDataDeps',
51
+ fn() {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ const resolver = (0, scan_1.createResolver)({
54
+ alias: api.config.alias,
55
+ });
56
+ api.appData.deps = yield (0, scan_1.scan)({
57
+ entry: (0, path_1.join)(api.paths.absTmpPath, 'umi.ts'),
58
+ externals: api.config.externals,
59
+ resolver,
60
+ });
61
+ // FIXME: force include react & react-dom
62
+ if (api.appData.deps['react']) {
63
+ api.appData.deps['react'].version = api.appData.react.version;
64
+ }
65
+ api.appData.deps['react-dom'] = {
66
+ version: api.appData.react.version,
67
+ matches: ['react-dom'],
68
+ subpaths: [],
69
+ };
70
+ });
71
+ },
72
+ });
46
73
  function getAppJsInfo() {
47
74
  return __awaiter(this, void 0, void 0, function* () {
48
75
  for (const path of (0, watch_1.expandJSPaths)((0, path_1.join)(api.paths.absSrcPath, 'app'))) {
@@ -16,8 +16,7 @@ function resolveProjectDep(opts) {
16
16
  exports.default = (api) => {
17
17
  const configDefaults = {
18
18
  alias: {
19
- umi: process.env.UMI_DIR,
20
- '@umijs/renderer-react': (0, path_1.dirname)(require.resolve('@umijs/renderer-react/package.json')),
19
+ umi: '@@/exports',
21
20
  react: resolveProjectDep({
22
21
  pkg: api.pkg,
23
22
  cwd: api.cwd,
@@ -36,6 +35,10 @@ exports.default = (api) => {
36
35
  publicPath: '/',
37
36
  mountElementId: 'root',
38
37
  base: '/',
38
+ history: { type: 'browser' },
39
+ targets: {
40
+ chrome: 87,
41
+ },
39
42
  };
40
43
  const bundleSchemas = (0, schema_1.getSchemas)();
41
44
  const extraSchemas = (0, schema_2.getSchemas)();
@@ -5,14 +5,24 @@ const utils_1 = require("@umijs/utils");
5
5
  function getSchemas() {
6
6
  return {
7
7
  base: (Joi) => Joi.string(),
8
- plugins: (Joi) => Joi.array().items(Joi.string()),
9
- publicPath: (Joi) => Joi.string().regex(/\/$/).error(new Error('publicPath must end with /')),
8
+ conventionRoutes: (Joi) => Joi.object({
9
+ exclude: Joi.array().items(Joi.any()),
10
+ base: Joi.string(),
11
+ }),
10
12
  favicon: (Joi) => Joi.string(),
11
- headScripts: (Joi) => Joi.array().items(Joi.alternatives(Joi.string())),
12
- scripts: (Joi) => Joi.array().items(Joi.alternatives(Joi.string())),
13
+ headScripts: (Joi) => Joi.array(),
14
+ history: (Joi) => Joi.object({
15
+ type: Joi.string().valid('browser', 'hash', 'memory'),
16
+ }),
17
+ links: (Joi) => Joi.array(),
18
+ metas: (Joi) => Joi.array(),
13
19
  mountElementId: (Joi) => Joi.string(),
14
20
  npmClient: (Joi) => Joi.string().valid(utils_1.NpmClientEnum.pnpm, utils_1.NpmClientEnum.tnpm, utils_1.NpmClientEnum.cnpm, utils_1.NpmClientEnum.yarn, utils_1.NpmClientEnum.npm),
21
+ plugins: (Joi) => Joi.array().items(Joi.string()),
22
+ publicPath: (Joi) => Joi.string().regex(/\/$/).error(new Error('publicPath must end with /')),
15
23
  routes: (Joi) => Joi.array().items(Joi.object()),
24
+ scripts: (Joi) => Joi.array(),
25
+ styles: (Joi) => Joi.array(),
16
26
  };
17
27
  }
18
28
  exports.getSchemas = getSchemas;
@@ -101,6 +101,7 @@ class ESMIService {
101
101
  });
102
102
  // get the build ticket id
103
103
  const ticketId = yield this.build(data);
104
+ utils_1.logger.info(`ticketId: ${ticketId}`);
104
105
  // continue to the next request after 2s
105
106
  const next = () => new Promise((resolve) => setTimeout(() => resolve(deferrer()), 2000));
106
107
  const deferrer = () => {
@@ -0,0 +1,8 @@
1
+ import { Plugin } from '@umijs/bundler-utils/compiled/esbuild';
2
+ import type { DepOptimizationOptions } from 'vite';
3
+ /**
4
+ * transform require call to import
5
+ */
6
+ export default function requireToImportPlugin({ exclude, }: {
7
+ exclude: NonNullable<DepOptimizationOptions['exclude']>;
8
+ }): Plugin;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const lodash_1 = require("@umijs/utils/compiled/lodash");
13
+ /**
14
+ * transform require call to import
15
+ */
16
+ function requireToImportPlugin({ exclude, }) {
17
+ const regSafeExclude = exclude.map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
18
+ const requireRegExp = new RegExp(`^(${regSafeExclude.join('|')})$`);
19
+ return {
20
+ name: 'preset-umi:esmi-require-to-import',
21
+ setup(build) {
22
+ // handler require calls for external deps
23
+ build.onResolve({
24
+ filter: requireRegExp,
25
+ }, (args) => __awaiter(this, void 0, void 0, function* () {
26
+ if (args.kind === 'require-call') {
27
+ return {
28
+ path: args.path,
29
+ namespace: 'esmi-require-to-import',
30
+ pluginData: {
31
+ resolveDir: args.resolveDir,
32
+ },
33
+ };
34
+ }
35
+ }));
36
+ // replace load content
37
+ build.onLoad({
38
+ filter: /.*/,
39
+ namespace: 'esmi-require-to-import',
40
+ }, (args) => {
41
+ const { resolveDir } = args.pluginData || {};
42
+ const packageName = args.path;
43
+ const starSpecifier = `${(0, lodash_1.camelCase)(packageName)}Star`;
44
+ const defaultSpecifier = `${(0, lodash_1.camelCase)(packageName)}Default`;
45
+ return {
46
+ resolveDir,
47
+ contents: [
48
+ `import * as ${starSpecifier} from '${packageName}';`,
49
+ '',
50
+ `const ${defaultSpecifier} = ${starSpecifier}.default ? ${starSpecifier}.default : ${starSpecifier};`,
51
+ '',
52
+ `export default ${defaultSpecifier};`,
53
+ `export * from '${packageName}';`,
54
+ '',
55
+ ].join('\n'),
56
+ };
57
+ });
58
+ },
59
+ };
60
+ }
61
+ exports.default = requireToImportPlugin;
@@ -0,0 +1,13 @@
1
+ import { Plugin } from '@umijs/bundler-utils/compiled/esbuild';
2
+ import type { DepOptimizationOptions } from 'vite';
3
+ import type { createResolver } from '../../../libs/scan';
4
+ /**
5
+ * only external top level import, exclude sub-path imports for esmi
6
+ * example:
7
+ * - import from 'antd' will be externalized
8
+ * - import from 'antd/dist/antd.less' will not be externalized
9
+ */
10
+ export default function topLevelExternal({ exclude, resolver, }: {
11
+ exclude: NonNullable<DepOptimizationOptions['exclude']>;
12
+ resolver: ReturnType<typeof createResolver>;
13
+ }): Plugin;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ /**
13
+ * only external top level import, exclude sub-path imports for esmi
14
+ * example:
15
+ * - import from 'antd' will be externalized
16
+ * - import from 'antd/dist/antd.less' will not be externalized
17
+ */
18
+ function topLevelExternal({ exclude, resolver, }) {
19
+ const regSafeExclude = exclude.map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
20
+ const subImportRegExp = new RegExp(`^(${regSafeExclude.join('|')})/`);
21
+ const extRegExp = /\.((?<!d)\.ts|jsx?|tsx)$/;
22
+ return {
23
+ name: 'preset-umi:esmi-top-level-external',
24
+ setup(build) {
25
+ build.onResolve({
26
+ filter: subImportRegExp,
27
+ }, (args) => __awaiter(this, void 0, void 0, function* () {
28
+ const resolved = yield resolver.resolve(args.resolveDir, args.path);
29
+ // only process javascript-like files
30
+ if (extRegExp.test(resolved)) {
31
+ return { path: resolved };
32
+ }
33
+ }));
34
+ },
35
+ };
36
+ }
37
+ exports.default = topLevelExternal;
@@ -16,8 +16,11 @@ const es_module_lexer_1 = require("@umijs/bundler-utils/compiled/es-module-lexer
16
16
  const magic_string_1 = __importDefault(require("magic-string"));
17
17
  const path_1 = require("path");
18
18
  const scan_1 = require("../../libs/scan");
19
+ const requireToImport_1 = __importDefault(require("./esbuildPlugins/requireToImport"));
20
+ const topLevelExternal_1 = __importDefault(require("./esbuildPlugins/topLevelExternal"));
19
21
  const Service_1 = __importDefault(require("./Service"));
20
22
  let importmap = { imports: {}, scopes: {} };
23
+ let importmatches = {};
21
24
  /**
22
25
  * esmi vite plugin
23
26
  */
@@ -25,13 +28,36 @@ function esmi(opts) {
25
28
  return {
26
29
  name: 'preset-umi:esmi',
27
30
  configResolved(config) {
31
+ var _a, _b;
32
+ var _c, _d;
28
33
  const { include, exclude } = config.optimizeDeps;
34
+ (_a = (_c = config.optimizeDeps).include) !== null && _a !== void 0 ? _a : (_c.include = []);
29
35
  // do not pre-compile deps which will be loaded by importmap (for top-level deps)
30
36
  if (include === null || include === void 0 ? void 0 : include.length) {
31
- config.optimizeDeps.include = include.filter((item) => !importmap.imports[item]);
37
+ config.optimizeDeps.include = include.filter((item) => !importmatches[item] && !importmap.imports[item]);
32
38
  }
33
- // exclude pre-compile deps which within importmap by default (for nested deps)
34
- config.optimizeDeps.exclude = (exclude || []).concat(Object.keys(importmap.imports).filter((item) => exclude === null || exclude === void 0 ? void 0 : exclude.includes(item)));
39
+ // exclude pre-compile deps
40
+ config.optimizeDeps.exclude = [
41
+ ...new Set([
42
+ // deps from user config
43
+ ...(exclude || []),
44
+ // deps from local scan
45
+ ...Object.keys(importmatches),
46
+ // deps from esmi analyze result
47
+ ...Object.keys(importmap.imports),
48
+ ]),
49
+ ];
50
+ // apply esbuild plugins
51
+ (_b = (_d = config.optimizeDeps).esbuildOptions) !== null && _b !== void 0 ? _b : (_d.esbuildOptions = {});
52
+ config.optimizeDeps.esbuildOptions.plugins = [
53
+ // transform require call to import
54
+ (0, requireToImport_1.default)({ exclude: config.optimizeDeps.exclude }),
55
+ // make sure vite only external top-level npm imports, and resolve sub-path npm imports
56
+ (0, topLevelExternal_1.default)({
57
+ exclude: config.optimizeDeps.exclude,
58
+ resolver: opts.resolver,
59
+ }),
60
+ ].concat(config.optimizeDeps.esbuildOptions.plugins || []);
35
61
  },
36
62
  transform(source) {
37
63
  try {
@@ -42,9 +68,17 @@ function esmi(opts) {
42
68
  imports.forEach((item) => {
43
69
  const { n: specifier, s: start, e: end } = item;
44
70
  // replace npm package to CDN url for matched imports
45
- if (specifier && importmap.imports[specifier]) {
46
- s !== null && s !== void 0 ? s : (s = new magic_string_1.default(source));
47
- s.overwrite(start, end, importmap.imports[specifier]);
71
+ if (specifier) {
72
+ const replacement =
73
+ // search from local scan matches first (for alias)
74
+ (importmatches[specifier] &&
75
+ importmap.imports[importmatches[specifier]]) ||
76
+ // search from esmi analyze result
77
+ importmap.imports[specifier];
78
+ if (replacement) {
79
+ s !== null && s !== void 0 ? s : (s = new magic_string_1.default(source));
80
+ s.overwrite(start, end, replacement);
81
+ }
48
82
  }
49
83
  });
50
84
  return (s === null || s === void 0 ? void 0 : s.toString()) || source;
@@ -78,7 +112,11 @@ function generatePkgData(api) {
78
112
  name: 'default',
79
113
  path: 'es/index.js',
80
114
  from: '',
81
- deps: Object.entries(api.appData.deps).map(([name, version]) => ({
115
+ deps: Object.entries(api.appData.deps)
116
+ // only compile entry imports
117
+ .filter(([_, { matches }]) => matches.length)
118
+ // convert to esmi config
119
+ .map(([name, { version }]) => ({
82
120
  name,
83
121
  version,
84
122
  usedMap: {
@@ -102,23 +140,29 @@ exports.default = (api) => {
102
140
  return __awaiter(this, void 0, void 0, function* () {
103
141
  // scan and module graph
104
142
  // TODO: module graph
105
- api.appData.deps = yield (0, scan_1.scan)({
106
- entry: (0, path_1.join)(api.paths.absTmpPath, 'umi.ts'),
107
- externals: api.config.externals,
108
- resolver,
143
+ yield api.applyPlugins({
144
+ key: 'updateAppDataDeps',
145
+ type: api.ApplyPluginsType.event,
109
146
  });
110
147
  // skip umi by default
111
148
  delete api.appData.deps['umi'];
112
- // FIXME: force include react & react-dom
113
- api.appData.deps['react'] = api.appData.react.version;
114
- api.appData.deps['react-dom'] = api.appData.react.version;
115
149
  const data = generatePkgData(api);
116
150
  const deps = data.pkgInfo.exports.reduce((r, exp) => r.concat(exp.deps.map((dep) => dep.name)), []);
117
151
  const hasNewDep = deps.some((i) => !importmap.imports[i]);
118
152
  // update importmap from esm if there has new import
119
153
  if (hasNewDep) {
120
- // TODO: add local cache and restore
121
154
  importmap = (_a = (yield service.getImportmap(data))) === null || _a === void 0 ? void 0 : _a.importMap;
155
+ // update matches map to dep name
156
+ importmatches = Object.keys(api.appData.deps).reduce((r, dep) => {
157
+ // filter subpath imports
158
+ if (!api.appData.deps[dep].subpaths.length) {
159
+ // map all matches to dep name
160
+ api.appData.deps[dep].matches.forEach((m) => {
161
+ r[m] = dep;
162
+ });
163
+ }
164
+ return r;
165
+ }, {});
122
166
  // because we will replaced package name to CDN url in vite plugin
123
167
  // so we must append scope rules for the CDN url like the import specifier
124
168
  // example:
@@ -194,6 +238,7 @@ exports.default = (api) => {
194
238
  yield refreshImportMap();
195
239
  // TODO: refresh page when importmap changed
196
240
  }),
241
+ resolver,
197
242
  }));
198
243
  return memo;
199
244
  });
@@ -37,15 +37,27 @@ function default_1() {
37
37
  t.isImportNamespaceSpecifier(parentNode)) {
38
38
  return;
39
39
  }
40
- if ((_a = state.opts.opts.identifierToLib) === null || _a === void 0 ? void 0 : _a[name]) {
40
+ // don't support member expression
41
+ // e.g. foo.styles
42
+ if (t.isMemberExpression(parentNode) &&
43
+ path.node === parentNode.property) {
44
+ return;
45
+ }
46
+ // don't support object property
47
+ // e.g. { styles: 1 }
48
+ if (t.isObjectProperty(parentNode) && path.node === parentNode.key) {
49
+ return;
50
+ }
51
+ // TODO: 替换方式需要缓存,避免过多临时变量
52
+ if ((_a = state.opts.opts.identifierToLib) === null || _a === void 0 ? void 0 : _a.hasOwnProperty(name)) {
41
53
  path.replaceWith((0, helper_module_imports_1.addNamed)(path, name, state.opts.opts.identifierToLib[name]));
42
54
  }
43
- else if ((_b = state.opts.opts.defaultToLib) === null || _b === void 0 ? void 0 : _b[name]) {
55
+ else if ((_b = state.opts.opts.defaultToLib) === null || _b === void 0 ? void 0 : _b.hasOwnProperty(name)) {
44
56
  path.replaceWith((0, helper_module_imports_1.addDefault)(path, state.opts.opts.defaultToLib[name], {
45
57
  nameHint: name,
46
58
  }));
47
59
  }
48
- else if ((_c = state.opts.opts.namespaceToLib) === null || _c === void 0 ? void 0 : _c[name]) {
60
+ else if ((_c = state.opts.opts.namespaceToLib) === null || _c === void 0 ? void 0 : _c.hasOwnProperty(name)) {
49
61
  path.replaceWith((0, helper_module_imports_1.addNamespace)(path, state.opts.opts.namespaceToLib[name]));
50
62
  }
51
63
  // import css
@@ -56,6 +68,7 @@ function default_1() {
56
68
  }
57
69
  },
58
70
  MemberExpression(path, state) {
71
+ var _a, _b;
59
72
  const { object, property } = path.node;
60
73
  if (path.scope.hasBinding(object.name)) {
61
74
  return;
@@ -66,8 +79,8 @@ function default_1() {
66
79
  t.isImportNamespaceSpecifier(parentNode)) {
67
80
  return;
68
81
  }
69
- if (state.opts.opts.withObjs[object.name] &&
70
- (state.opts.opts.withObjs[object.name].members || []).includes(property.name)) {
82
+ if (((_a = state.opts.opts.withObjs) === null || _a === void 0 ? void 0 : _a[object.name]) &&
83
+ (((_b = state.opts.opts.withObjs) === null || _b === void 0 ? void 0 : _b[object.name].members) || []).includes(property.name)) {
71
84
  path.replaceWith((0, helper_module_imports_1.addNamed)(path, property.name, state.opts.opts.withObjs[object.name].importFrom));
72
85
  }
73
86
  },
@@ -6,4 +6,8 @@ export interface IMock {
6
6
  }
7
7
  export declare function getMockData(opts: {
8
8
  cwd: string;
9
+ mockConfig: {
10
+ exclude?: string[];
11
+ include?: string[];
12
+ };
9
13
  }): Record<string, IMock>;
@@ -13,12 +13,20 @@ function getMockData(opts) {
13
13
  implementor: esbuild_1.default,
14
14
  });
15
15
  utils_1.register.clearFiles();
16
- const ret = utils_1.glob
17
- .sync(constants_1.MOCK_FILE_GLOB, { cwd: opts.cwd })
16
+ function normalizeMockFile(file) {
17
+ const cwd = opts.cwd.endsWith('/') ? opts.cwd : `${opts.cwd}/`;
18
+ return utils_1.chalk.yellow(file.replace(cwd, ''));
19
+ }
20
+ const ret = [constants_1.MOCK_FILE_GLOB, ...(opts.mockConfig.include || [])]
21
+ .reduce((memo, pattern) => {
22
+ memo.push(...utils_1.glob.sync(pattern, { cwd: opts.cwd, ignore: ['**/*.d.ts'] }));
23
+ return memo;
24
+ }, [])
18
25
  .reduce((memo, file) => {
19
26
  const mockFile = `${opts.cwd}/${file}`;
20
27
  const m = require(mockFile);
21
- const obj = m.default;
28
+ // Cannot convert undefined or null to object
29
+ const obj = (m === null || m === void 0 ? void 0 : m.default) || {};
22
30
  for (const key of Object.keys(obj)) {
23
31
  const mock = getMock({ key, obj });
24
32
  mock.file = mockFile;
@@ -28,7 +36,7 @@ function getMockData(opts) {
28
36
  utils_1.lodash.isPlainObject(mock.handler) ||
29
37
  typeof mock.handler === 'function', `Mock handler must be function or array or object, but got ${typeof mock.handler} for ${mock.method} in ${mock.file}`);
30
38
  if (memo[id]) {
31
- throw new Error(`${id} is duplicated in ${mockFile} and ${memo[id].file}`);
39
+ utils_1.logger.warn(`${id} is duplicated in ${normalizeMockFile(mockFile)} and ${normalizeMockFile(memo[id].file)}`);
32
40
  }
33
41
  memo[id] = mock;
34
42
  }
@@ -47,7 +55,7 @@ function getMock(opts) {
47
55
  return { method, path, handler };
48
56
  }
49
57
  function parseKey(key) {
50
- const spliced = key.split(' ');
58
+ const spliced = key.split(/\s+/);
51
59
  const len = spliced.length;
52
60
  if (len === 1) {
53
61
  return { method: constants_1.DEFAULT_METHOD, path: key };
@@ -56,6 +64,7 @@ function parseKey(key) {
56
64
  const [method, path] = spliced;
57
65
  const upperCaseMethod = method.toUpperCase();
58
66
  (0, assert_1.default)(constants_1.VALID_METHODS.includes(upperCaseMethod), `method ${method} is not supported`);
67
+ (0, assert_1.default)(path, `${key}, path is undefined`);
59
68
  return { method: upperCaseMethod, path };
60
69
  }
61
70
  }
@@ -21,6 +21,7 @@ function default_1(api) {
21
21
  exclude: Joi.array()
22
22
  .items(Joi.string())
23
23
  .description('exclude files not parse mock'),
24
+ include: Joi.array().items(Joi.string()),
24
25
  });
25
26
  },
26
27
  },
@@ -37,12 +38,18 @@ function default_1(api) {
37
38
  path: `${api.cwd}/mock`,
38
39
  addToUnWatches: true,
39
40
  onChange: () => {
40
- context.mockData = (0, getMockData_1.getMockData)({ cwd: api.cwd });
41
+ context.mockData = (0, getMockData_1.getMockData)({
42
+ cwd: api.cwd,
43
+ mockConfig: api.config.mock || {},
44
+ });
41
45
  },
42
46
  });
43
47
  });
44
48
  api.addBeforeMiddlewares(() => __awaiter(this, void 0, void 0, function* () {
45
- context.mockData = (0, getMockData_1.getMockData)({ cwd: api.cwd });
49
+ context.mockData = (0, getMockData_1.getMockData)({
50
+ cwd: api.cwd,
51
+ mockConfig: api.config.mock || {},
52
+ });
46
53
  return [
47
54
  (0, createMockMiddleware_1.createMockMiddleware)({
48
55
  context,
@@ -1,14 +1,56 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@umijs/bundler-utils/compiled/babel/core");
4
+ const path_1 = require("path");
3
5
  exports.default = (api) => {
4
- api.addPolyfillImports(() => {
5
- var _a;
6
- return api.config.publicPath === 'auto' && ((_a = api.config.targets) === null || _a === void 0 ? void 0 : _a.ie)
7
- ? [
8
- {
9
- source: require.resolve('current-script-polyfill'),
10
- },
11
- ]
12
- : [];
6
+ api.describe({
7
+ key: 'polyfill',
8
+ config: {
9
+ schema(Joi) {
10
+ return Joi.object().keys({
11
+ imports: Joi.array().items(Joi.string()).required().unique(),
12
+ });
13
+ },
14
+ },
15
+ enableBy: () => {
16
+ return process.env.BABEL_POLYFILL !== 'none';
17
+ },
18
+ });
19
+ api.onGenerateFiles(() => {
20
+ var _a, _b;
21
+ const coreJsImports = ((_a = api.config.polyfill) === null || _a === void 0 ? void 0 : _a.imports)
22
+ ? (_b = api.config.polyfill) === null || _b === void 0 ? void 0 : _b.imports.map((item) => `import '${item}';`).join('\n')
23
+ : `import 'core-js';`;
24
+ const { code } = (0, core_1.transform)(`
25
+ ${coreJsImports}
26
+ import 'regenerator-runtime/runtime';
27
+ export {};
28
+ `, {
29
+ filename: 'polyfill.ts',
30
+ presets: [
31
+ [
32
+ require.resolve('@umijs/bundler-utils/compiled/babel/preset-env'),
33
+ {
34
+ useBuiltIns: 'entry',
35
+ corejs: '3',
36
+ modules: false,
37
+ targets: api.config.targets,
38
+ },
39
+ ],
40
+ ],
41
+ plugins: [
42
+ require.resolve('@umijs/babel-preset-umi/dist/plugins/lockCoreJS'),
43
+ ],
44
+ });
45
+ api.writeTmpFile({
46
+ path: 'core/polyfill.ts',
47
+ noPluginDir: true,
48
+ content: code,
49
+ });
50
+ });
51
+ api.addPolyfillImports(() => [{ source: `./core/polyfill` }]);
52
+ api.modifyConfig((memo) => {
53
+ memo.alias['regenerator-runtime'] = (0, path_1.dirname)(require.resolve('regenerator-runtime/package'));
54
+ return memo;
13
55
  });
14
56
  };
@@ -0,0 +1,3 @@
1
+ import { IApi } from '../../types';
2
+ declare const _default: (api: IApi) => void;
3
+ export default _default;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (api) => {
4
+ api.addPolyfillImports(() => {
5
+ var _a;
6
+ return api.config.publicPath === 'auto' && ((_a = api.config.targets) === null || _a === void 0 ? void 0 : _a.ie)
7
+ ? [
8
+ {
9
+ source: require.resolve('current-script-polyfill'),
10
+ },
11
+ ]
12
+ : [];
13
+ });
14
+ };
@@ -16,6 +16,7 @@ const fs_1 = require("fs");
16
16
  const path_1 = require("path");
17
17
  // get route config
18
18
  function getRoutes(opts) {
19
+ var _a, _b, _c;
19
20
  return __awaiter(this, void 0, void 0, function* () {
20
21
  let routes = null;
21
22
  if (opts.api.config.routes) {
@@ -25,10 +26,33 @@ function getRoutes(opts) {
25
26
  }
26
27
  else {
27
28
  routes = (0, core_1.getConventionRoutes)({
28
- base: opts.api.paths.absPagesPath,
29
+ base: ((_a = opts.api.config.conventionRoutes) === null || _a === void 0 ? void 0 : _a.base) || opts.api.paths.absPagesPath,
30
+ exclude: (_b = opts.api.config.conventionRoutes) === null || _b === void 0 ? void 0 : _b.exclude,
29
31
  prefix: '',
30
32
  });
31
33
  }
34
+ function localPath(path) {
35
+ if (path.charAt(0) !== '.') {
36
+ return `./${path}`;
37
+ }
38
+ {
39
+ return path;
40
+ }
41
+ }
42
+ for (const id of Object.keys(routes)) {
43
+ if (routes[id].file) {
44
+ // TODO: cache for performance
45
+ const file = (0, path_1.isAbsolute)(routes[id].file)
46
+ ? routes[id].file
47
+ : utils_1.resolve.sync(localPath(routes[id].file), {
48
+ basedir: ((_c = opts.api.config.conventionRoutes) === null || _c === void 0 ? void 0 : _c.base) ||
49
+ opts.api.paths.absPagesPath,
50
+ extensions: ['.js', '.jsx', '.tsx', '.ts'],
51
+ });
52
+ routes[id].__content = (0, fs_1.readFileSync)(file, 'utf-8');
53
+ }
54
+ }
55
+ // layout routes
32
56
  const absLayoutPath = (0, path_1.join)(opts.api.paths.absSrcPath, 'layouts/index.tsx');
33
57
  const layouts = yield opts.api.applyPlugins({
34
58
  key: 'addLayouts',
@@ -49,6 +73,16 @@ function getRoutes(opts) {
49
73
  parentId: undefined,
50
74
  },
51
75
  routes,
76
+ test: layout.test,
77
+ });
78
+ }
79
+ // patch routes
80
+ for (const id of Object.keys(routes)) {
81
+ yield opts.api.applyPlugins({
82
+ key: 'onPatchRoute',
83
+ args: {
84
+ route: routes[id],
85
+ },
52
86
  });
53
87
  }
54
88
  routes = yield opts.api.applyPlugins({
@@ -64,8 +98,9 @@ function getRouteComponents(opts) {
64
98
  const imports = Object.keys(opts.routes)
65
99
  .map((key) => {
66
100
  const route = opts.routes[key];
67
- if (!route.file)
68
- return `// ${key}: no file to import`;
101
+ if (!route.file) {
102
+ return `'${key}': () => import('./EmptyRoute'),`;
103
+ }
69
104
  // e.g.
70
105
  // component: () => <h1>foo</h1>
71
106
  // component: (() => () => <h1>foo</h1>)()
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ const bundler_utils_1 = require("@umijs/bundler-utils");
12
13
  const utils_1 = require("@umijs/utils");
13
14
  const fs_1 = require("fs");
14
15
  const path_1 = require("path");
@@ -25,6 +26,11 @@ exports.default = (api) => {
25
26
  },
26
27
  });
27
28
  api.onGenerateFiles((opts) => __awaiter(void 0, void 0, void 0, function* () {
29
+ var _a;
30
+ const rendererPath = yield api.applyPlugins({
31
+ key: 'modifyRendererPath',
32
+ initialValue: (0, path_1.dirname)(require.resolve('@umijs/renderer-react/package.json')),
33
+ });
28
34
  // umi.ts
29
35
  api.writeTmpFile({
30
36
  noPluginDir: true,
@@ -32,10 +38,7 @@ exports.default = (api) => {
32
38
  tplPath: (0, path_1.join)(constants_1.TEMPLATES_DIR, 'umi.tpl'),
33
39
  context: {
34
40
  mountElementId: api.config.mountElementId,
35
- rendererPath: yield api.applyPlugins({
36
- key: 'modifyRendererPath',
37
- initialValue: '@umijs/renderer-react',
38
- }),
41
+ rendererPath,
39
42
  entryCode: (yield api.applyPlugins({
40
43
  key: 'addEntryCode',
41
44
  initialValue: [],
@@ -57,8 +60,20 @@ exports.default = (api) => {
57
60
  initialValue: [],
58
61
  })).join('\n'),
59
62
  basename: api.config.base,
63
+ historyType: api.config.history.type,
60
64
  },
61
65
  });
66
+ // EmptyRoutes.tsx
67
+ api.writeTmpFile({
68
+ noPluginDir: true,
69
+ path: 'core/EmptyRoute.tsx',
70
+ content: `
71
+ import { Outlet } from 'umi';
72
+ export default function EmptyRoute() {
73
+ return <Outlet />;
74
+ }
75
+ `,
76
+ });
62
77
  // route.ts
63
78
  let routes;
64
79
  if (opts.isFirstTime) {
@@ -71,13 +86,22 @@ exports.default = (api) => {
71
86
  }
72
87
  const hasSrc = api.appData.hasSrcDir;
73
88
  // @/pages/
74
- const prefix = hasSrc ? '../../../src/pages/' : '../../pages/';
89
+ const pages = (0, path_1.basename)(((_a = api.config.conventionRoutes) === null || _a === void 0 ? void 0 : _a.base) || api.paths.absPagesPath);
90
+ const prefix = hasSrc ? `../../../src/${pages}/` : `../../${pages}/`;
91
+ const clonedRoutes = utils_1.lodash.cloneDeep(routes);
92
+ for (const id of Object.keys(clonedRoutes)) {
93
+ for (const key of Object.keys(clonedRoutes[id])) {
94
+ if (key.startsWith('__')) {
95
+ delete clonedRoutes[id][key];
96
+ }
97
+ }
98
+ }
75
99
  api.writeTmpFile({
76
100
  noPluginDir: true,
77
101
  path: 'core/route.tsx',
78
102
  tplPath: (0, path_1.join)(constants_1.TEMPLATES_DIR, 'route.tpl'),
79
103
  context: {
80
- routes: JSON.stringify(routes),
104
+ routes: JSON.stringify(clonedRoutes),
81
105
  routeComponents: yield (0, routes_1.getRouteComponents)({ routes, prefix }),
82
106
  },
83
107
  });
@@ -111,7 +135,7 @@ exports.default = (api) => {
111
135
  'dataflowProvider',
112
136
  'outerProvider',
113
137
  'render',
114
- // 'onRouteChange',
138
+ 'onRouteChange',
115
139
  ],
116
140
  });
117
141
  api.writeTmpFile({
@@ -126,5 +150,73 @@ exports.default = (api) => {
126
150
  validKeys: validKeys,
127
151
  },
128
152
  });
153
+ // history.ts
154
+ api.writeTmpFile({
155
+ noPluginDir: true,
156
+ path: 'core/history.ts',
157
+ tplPath: (0, path_1.join)(constants_1.TEMPLATES_DIR, 'history.tpl'),
158
+ context: {
159
+ rendererPath,
160
+ },
161
+ });
129
162
  }));
163
+ function getExports(opts) {
164
+ return __awaiter(this, void 0, void 0, function* () {
165
+ const content = (0, fs_1.readFileSync)(opts.path, 'utf-8');
166
+ const [_, exports] = yield (0, bundler_utils_1.parseModule)({ content, path: opts.path });
167
+ return exports || [];
168
+ });
169
+ }
170
+ // Generate @@/exports.ts
171
+ api.register({
172
+ key: 'onGenerateFiles',
173
+ fn: () => __awaiter(void 0, void 0, void 0, function* () {
174
+ const exports = [];
175
+ // @umijs/renderer-react
176
+ exports.push('// @umijs/renderer-react');
177
+ const rendererReactPath = (0, path_1.dirname)(require.resolve('@umijs/renderer-react/package.json'));
178
+ exports.push(`export { ${(yield getExports({
179
+ path: (0, path_1.join)(rendererReactPath, 'dist/index.js'),
180
+ })).join(', ')} } from '${rendererReactPath}';`);
181
+ // umi/client/client/plugin
182
+ exports.push('// umi/client/client/plugin');
183
+ const umiDir = process.env.UMI_DIR;
184
+ const umiPluginPath = (0, path_1.join)(umiDir, 'client/client/plugin.js');
185
+ exports.push(`export { ${(yield getExports({
186
+ path: umiPluginPath,
187
+ })).join(', ')} } from '${umiPluginPath}';`);
188
+ // @@/core/history.ts
189
+ exports.push(`export { history, createHistory } from './core/history';`);
190
+ // plugins
191
+ exports.push('// plugins');
192
+ const plugins = (0, fs_1.readdirSync)(api.paths.absTmpPath).filter((file) => {
193
+ if (file.startsWith('plugin-') &&
194
+ ((0, fs_1.existsSync)((0, path_1.join)(api.paths.absTmpPath, file, 'index.ts')) ||
195
+ (0, fs_1.existsSync)((0, path_1.join)(api.paths.absTmpPath, file, 'index.tsx')))) {
196
+ return true;
197
+ }
198
+ });
199
+ for (const plugin of plugins) {
200
+ let file;
201
+ if ((0, fs_1.existsSync)((0, path_1.join)(api.paths.absTmpPath, plugin, 'index.ts'))) {
202
+ file = (0, path_1.join)(api.paths.absTmpPath, plugin, 'index.ts');
203
+ }
204
+ if ((0, fs_1.existsSync)((0, path_1.join)(api.paths.absTmpPath, plugin, 'index.tsx'))) {
205
+ file = (0, path_1.join)(api.paths.absTmpPath, plugin, 'index.tsx');
206
+ }
207
+ const pluginExports = yield getExports({
208
+ path: file,
209
+ });
210
+ if (pluginExports.length) {
211
+ exports.push(`export { ${pluginExports.join(', ')} } from '${(0, path_1.join)(api.paths.absTmpPath, plugin)}';`);
212
+ }
213
+ }
214
+ api.writeTmpFile({
215
+ noPluginDir: true,
216
+ path: 'exports.ts',
217
+ content: exports.join('\n'),
218
+ });
219
+ }),
220
+ stage: Infinity,
221
+ });
130
222
  };
@@ -0,0 +1,3 @@
1
+ import type { IApi } from '../../types';
2
+ declare const _default: (api: IApi) => void;
3
+ export default _default;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.default = (api) => {
13
+ // scan deps into api.appData by default for vite mode
14
+ api.register({
15
+ key: 'onBeforeCompiler',
16
+ stage: Number.POSITIVE_INFINITY,
17
+ fn() {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ yield api.applyPlugins({
20
+ key: 'updateAppDataDeps',
21
+ type: api.ApplyPluginsType.event,
22
+ });
23
+ });
24
+ },
25
+ });
26
+ // include extra monorepo package deps for vite pre-bundle
27
+ api.modifyViteConfig((memo) => {
28
+ var _a, _b;
29
+ memo.optimizeDeps = Object.assign(Object.assign({}, (memo.optimizeDeps || {})), { include: (_b = (_a = memo.optimizeDeps) === null || _a === void 0 ? void 0 : _a.include) === null || _b === void 0 ? void 0 : _b.concat(Object.values(api.appData.deps)
30
+ .map(({ matches }) => matches[0])
31
+ .filter((item) => (item === null || item === void 0 ? void 0 : item.startsWith('@fs')) && !(item === null || item === void 0 ? void 0 : item.includes('node_modules')))) });
32
+ return memo;
33
+ });
34
+ };
package/dist/index.js CHANGED
@@ -13,9 +13,11 @@ exports.default = () => {
13
13
  require.resolve('./features/favicon/favicon'),
14
14
  require.resolve('./features/mock/mock'),
15
15
  require.resolve('./features/polyfill/polyfill'),
16
+ require.resolve('./features/polyfill/publicPathPolyfill'),
16
17
  require.resolve('./features/tmpFiles/tmpFiles'),
17
18
  require.resolve('./features/transform/transform'),
18
19
  require.resolve('./features/lowImport/lowImport'),
20
+ require.resolve('./features/vite/vite'),
19
21
  // commands
20
22
  require.resolve('./commands/build'),
21
23
  require.resolve('./commands/config/config'),
@@ -24,5 +24,9 @@ export declare function scan(opts: {
24
24
  entry: string;
25
25
  externals: any;
26
26
  resolver: any;
27
- }): Promise<Record<string, string>>;
27
+ }): Promise<Record<string, {
28
+ version: string;
29
+ matches: string[];
30
+ subpaths: string[];
31
+ }>>;
28
32
  export {};
package/dist/libs/scan.js CHANGED
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.scan = exports.createResolver = exports.getContent = exports.scanContent = void 0;
16
+ const bundler_utils_1 = require("@umijs/bundler-utils");
16
17
  const es_module_lexer_1 = require("@umijs/bundler-utils/compiled/es-module-lexer");
17
18
  const esbuild_1 = require("@umijs/bundler-utils/compiled/esbuild");
18
19
  const utils_1 = require("@umijs/utils");
@@ -31,7 +32,22 @@ function scanContent(opts) {
31
32
  return __awaiter(this, void 0, void 0, function* () {
32
33
  yield es_module_lexer_1.init;
33
34
  const [imports] = (0, es_module_lexer_1.parse)(opts.content);
34
- const deps = imports.map((imp) => {
35
+ const deps = imports
36
+ .filter(
37
+ // exclude all type-only deps
38
+ (imp) => {
39
+ const stmt = opts.content.slice(imp.ss, imp.se);
40
+ return (
41
+ // skip dynamicImport
42
+ imp.d > -1 ||
43
+ // import a from or import a,
44
+ /^import\s+[a-zA-Z_$][\w_$]*(\s+from|\s*,)/.test(stmt) ||
45
+ // export a from or export *
46
+ /^export\s+([a-zA-Z_$][\w_$]*\s+from|\*)/.test(stmt) ||
47
+ // { a, type b } or { type a, b }
48
+ /(?<!type\s+){(\s*(?!type)[a-zA-Z_$]|.*,\s*(?!type)[a-zA-Z_$])/.test(stmt));
49
+ })
50
+ .map((imp) => {
35
51
  let importType = ImportType.import;
36
52
  if (imp.d > -1)
37
53
  importType = ImportType.dynamicImport;
@@ -79,6 +95,7 @@ function createResolver(opts) {
79
95
  }
80
96
  exports.createResolver = createResolver;
81
97
  function scan(opts) {
98
+ var _a, _b;
82
99
  return __awaiter(this, void 0, void 0, function* () {
83
100
  const cache = new Map();
84
101
  const queueDeps = [opts.entry];
@@ -93,12 +110,49 @@ function scan(opts) {
93
110
  cache.set(depPath, deps);
94
111
  for (const dep of deps) {
95
112
  const resolved = yield opts.resolver.resolve((0, path_1.dirname)(depPath), dep.url);
96
- if (resolved.includes('node_modules') ||
97
- resolved.includes('umi-next/packages')) {
113
+ if ((0, bundler_utils_1.isDepPath)(resolved)) {
98
114
  const pkgPath = utils_1.pkgUp.sync({ cwd: resolved });
99
115
  (0, assert_1.default)(pkgPath, `package.json for found for ${resolved}`);
100
116
  const pkg = require(pkgPath);
101
- ret[pkg.name] = pkg.version;
117
+ const entryResolved = yield opts.resolver
118
+ .resolve((0, path_1.dirname)(pkgPath), '.')
119
+ // alias may resolve error (eg: dva from @umijs/plugins)
120
+ // fallback to null for mark it as subpath usage
121
+ .catch(() => null);
122
+ const isSubpath = entryResolved !== resolved;
123
+ ret[pkg.name] = {
124
+ version: pkg.version,
125
+ // collect entry matches
126
+ matches: [
127
+ // avoid duplicate
128
+ ...new Set([
129
+ ...(((_a = ret[pkg.name]) === null || _a === void 0 ? void 0 : _a.matches) || []),
130
+ // only collect non-subpath matches
131
+ ...(!isSubpath
132
+ ? [
133
+ // match origin path from source code
134
+ dep.url,
135
+ // match resolved absolute path
136
+ resolved,
137
+ // match no ext name path
138
+ resolved.replace(/\/\.[^\.]+$/, ''),
139
+ // match parent dir for index module
140
+ ...(/\/index[^\/]+$/.test(resolved)
141
+ ? [(0, path_1.dirname)(resolved)]
142
+ : []),
143
+ ]
144
+ : []),
145
+ ]),
146
+ ],
147
+ // collect subpath matches
148
+ subpaths: [
149
+ // avoid duplicate
150
+ ...new Set([
151
+ ...(((_b = ret[pkg.name]) === null || _b === void 0 ? void 0 : _b.subpaths) || []),
152
+ ...(isSubpath ? [dep.url] : []),
153
+ ]),
154
+ ],
155
+ };
102
156
  }
103
157
  else if (['.ts', '.tsx', '.js', '.jsx', '.mjs'].includes((0, path_1.extname)(resolved))) {
104
158
  queueDeps.push(resolved);
@@ -7,14 +7,14 @@ const utils_1 = require("@umijs/utils");
7
7
  const assert_1 = __importDefault(require("assert"));
8
8
  const fs_1 = require("fs");
9
9
  const path_1 = require("path");
10
- const transformIEAR_1 = __importDefault(require("./utils/transformIEAR"));
11
10
  const isTypeScriptFile_1 = require("./utils/isTypeScriptFile");
11
+ const transformIEAR_1 = __importDefault(require("./utils/transformIEAR"));
12
12
  exports.default = (api) => {
13
13
  [
14
14
  'onGenerateFiles',
15
15
  'onBeforeCompiler',
16
16
  'onBuildComplete',
17
- // 'onPatchRoute',
17
+ 'onPatchRoute',
18
18
  // 'onPatchRouteBefore',
19
19
  // 'onPatchRoutes',
20
20
  // 'onPatchRoutesBefore',
package/dist/types.d.ts CHANGED
@@ -101,6 +101,9 @@ export declare type IApi = PluginAPI & IServicePluginAPI & {
101
101
  stats: any;
102
102
  time: number;
103
103
  }>;
104
+ onPatchRoute: IEvent<{
105
+ route: IRoute;
106
+ }>;
104
107
  addEntryImports: IAdd<null, IEntryImport[]>;
105
108
  addEntryImportsAhead: IAdd<null, IEntryImport[]>;
106
109
  addEntryCodeAhead: IAdd<null, string[]>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IEAR_REG_EXP = void 0;
4
+ const bundler_utils_1 = require("@umijs/bundler-utils");
4
5
  const utils_1 = require("@umijs/utils");
5
6
  const path_1 = require("path");
6
7
  /**
@@ -84,10 +85,14 @@ function transformIEAR({ content, path }, api) {
84
85
  return content.replace(exports.IEAR_REG_EXP, (_, prefix, quote, absPath) => {
85
86
  if (absPath.startsWith(api.paths.absTmpPath)) {
86
87
  // transform .umi absolute imports
87
- absPath = (0, utils_1.winPath)((0, path_1.relative)((0, path_1.dirname)(path), absPath));
88
+ absPath = (0, utils_1.winPath)((0, path_1.relative)((0, path_1.dirname)(path), absPath)).replace(
89
+ // prepend ./ for same or sub level imports
90
+ /^(?!\.\.\/)/, './');
88
91
  }
89
- else if (absPath.includes('node_modules')) {
92
+ else if ((0, bundler_utils_1.isDepPath)(absPath)) {
90
93
  // transform node_modules absolute imports
94
+ // why @fs
95
+ // 由于我们临时文件下大量绝对路径的引用,而绝对路径的引用不会被 Vite 预编译
91
96
  absPath = `@fs${absPath}`;
92
97
  }
93
98
  return `${prefix}${quote}${absPath}${quote}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umijs/preset-umi",
3
- "version": "4.0.0-beta.14",
3
+ "version": "4.0.0-beta.18",
4
4
  "description": "@umijs/preset-umi",
5
5
  "homepage": "https://github.com/umijs/umi-next/tree/master/packages/preset-umi#readme",
6
6
  "bugs": "https://github.com/umijs/umi-next/issues",
@@ -24,21 +24,22 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@types/multer": "1.4.7",
27
- "@umijs/ast": "4.0.0-beta.14",
28
- "@umijs/babel-preset-umi": "4.0.0-beta.14",
29
- "@umijs/bundler-utils": "4.0.0-beta.14",
30
- "@umijs/bundler-vite": "4.0.0-beta.14",
31
- "@umijs/bundler-webpack": "4.0.0-beta.14",
32
- "@umijs/core": "4.0.0-beta.14",
33
- "@umijs/renderer-react": "4.0.0-beta.14",
34
- "@umijs/server": "4.0.0-beta.14",
35
- "@umijs/utils": "4.0.0-beta.14",
27
+ "@umijs/ast": "4.0.0-beta.18",
28
+ "@umijs/babel-preset-umi": "4.0.0-beta.18",
29
+ "@umijs/bundler-utils": "4.0.0-beta.18",
30
+ "@umijs/bundler-vite": "4.0.0-beta.18",
31
+ "@umijs/bundler-webpack": "4.0.0-beta.18",
32
+ "@umijs/core": "4.0.0-beta.18",
33
+ "@umijs/renderer-react": "4.0.0-beta.18",
34
+ "@umijs/server": "4.0.0-beta.18",
35
+ "@umijs/utils": "4.0.0-beta.18",
36
+ "core-js": "3.19.2",
36
37
  "current-script-polyfill": "1.0.0",
37
38
  "enhanced-resolve": "5.8.3",
38
39
  "magic-string": "0.25.7",
39
40
  "path-to-regexp": "1.7.0",
40
- "react": "18.0.0-rc.0",
41
- "react-dom": "18.0.0-rc.0",
41
+ "react": "17.0.2",
42
+ "react-dom": "17.0.2",
42
43
  "react-router": "6.1.1",
43
44
  "react-router-dom": "6.1.1"
44
45
  },
@@ -0,0 +1,15 @@
1
+ import { createHashHistory, createMemoryHistory, createBrowserHistory, History } from '{{{ rendererPath }}}';
2
+
3
+ let history: History;
4
+ export function createHistory(opts: any) {
5
+ if (opts.type === 'hash') {
6
+ history = createHashHistory();
7
+ } else if (opts.type === 'memory') {
8
+ history = createMemoryHistory();
9
+ } else {
10
+ history = createBrowserHistory();
11
+ }
12
+ return history;
13
+ }
14
+
15
+ export { history };
package/templates/umi.tpl CHANGED
@@ -3,6 +3,7 @@
3
3
  import { renderClient } from '{{{ rendererPath }}}';
4
4
  import { getRoutes } from './core/route';
5
5
  import { createPluginManager } from './core/plugin';
6
+ import { createHistory } from './core/history';
6
7
  import { ApplyPluginsType, PluginManager } from 'umi';
7
8
  {{{ imports }}}
8
9
 
@@ -24,6 +25,9 @@ async function render() {
24
25
  routeComponents,
25
26
  pluginManager,
26
27
  rootElement: document.getElementById('{{{ mountElementId }}}'),
28
+ history: createHistory({
29
+ type: '{{{ historyType }}}',
30
+ }),
27
31
  {{#basename}}
28
32
  basename: '{{{ basename }}}',
29
33
  {{/basename}}