@umijs/preset-umi 4.0.0-beta.12 → 4.0.0-beta.13

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.
@@ -24,11 +24,15 @@ window.__vite_plugin_react_preamble_installed__ = true
24
24
  function createRouteMiddleware(opts) {
25
25
  return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
26
26
  const { vite } = opts.api.args;
27
+ const viteScripts = [
28
+ // add noshim attr for skip importmap shim logic for this modules
29
+ { content: viteRefreshScript, noshim: '' },
30
+ { src: '/@vite/client', noshim: '' },
31
+ '/.umi/umi.ts',
32
+ ];
27
33
  const markupArgs = yield (0, getMarkupArgs_1.getMarkupArgs)(opts);
28
34
  // @ts-ignore
29
- const requestHandler = yield (0, server_1.createRequestHandler)(Object.assign(Object.assign({}, markupArgs), { scripts: (vite
30
- ? [viteRefreshScript, '/@vite/client', '/.umi/umi.ts']
31
- : ['/umi.js']).concat(markupArgs.scripts), esmScript: vite }));
35
+ const requestHandler = yield (0, server_1.createRequestHandler)(Object.assign(Object.assign({}, markupArgs), { scripts: (vite ? viteScripts : ['/umi.js']).concat(markupArgs.scripts), esmScript: vite }));
32
36
  requestHandler(req, res, next);
33
37
  });
34
38
  }
@@ -9,8 +9,11 @@ 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");
14
+ const fs_1 = require("fs");
13
15
  const path_1 = require("path");
16
+ const watch_1 = require("../../commands/dev/watch");
14
17
  const routes_1 = require("../tmpFiles/routes");
15
18
  exports.default = (api) => {
16
19
  api.modifyAppData((memo) => __awaiter(void 0, void 0, void 0, function* () {
@@ -25,6 +28,36 @@ exports.default = (api) => {
25
28
  memo.react = {
26
29
  version: require((0, path_1.join)(api.config.alias.react, 'package.json')).version,
27
30
  };
31
+ memo.appJS = yield getAppJsInfo();
28
32
  return memo;
29
33
  }));
34
+ // Execute earliest, so that other onGenerateFiles can get it
35
+ api.register({
36
+ key: 'onGenerateFiles',
37
+ fn(args) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ if (!args.isFirstTime) {
40
+ api.appData.appJS = yield getAppJsInfo();
41
+ }
42
+ });
43
+ },
44
+ stage: Number.NEGATIVE_INFINITY,
45
+ });
46
+ function getAppJsInfo() {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ for (const path of (0, watch_1.expandJSPaths)((0, path_1.join)(api.paths.absSrcPath, 'app'))) {
49
+ if ((0, fs_1.existsSync)(path)) {
50
+ const [_, exports] = yield (0, bundler_utils_1.parseModule)({
51
+ path,
52
+ content: (0, fs_1.readFileSync)(path, 'utf-8'),
53
+ });
54
+ return {
55
+ path,
56
+ exports,
57
+ };
58
+ }
59
+ }
60
+ return null;
61
+ });
62
+ }
30
63
  };
@@ -31,4 +31,15 @@ exports.default = (api) => {
31
31
  },
32
32
  });
33
33
  }));
34
+ api.onCheckCode((args) => {
35
+ // Fixed version import is not allowed
36
+ // e.g. import { X } from '_@ant-design_icons@4.7.0@ant-design/icons'
37
+ if (['cnpm', 'tnpm'].includes(api.appData.npmClient)) {
38
+ args.imports.forEach(({ source }) => {
39
+ if (/@\d/.test(source)) {
40
+ throw new Error(`${source} is not allowed to import.`);
41
+ }
42
+ });
43
+ }
44
+ });
34
45
  };
@@ -38,9 +38,28 @@ export interface IPkgData {
38
38
  */
39
39
  export default class ESMIService {
40
40
  cdnOrigin: string;
41
+ cacheDir: string;
42
+ cache: Record<string, IImportmapData>;
41
43
  constructor(opts: {
42
44
  cdnOrigin: string;
45
+ cacheDir: string;
43
46
  });
47
+ /**
48
+ * get cache file path by cache key
49
+ * @param data pkg data
50
+ */
51
+ static getCacheKey(data: IPkgData): string;
52
+ /**
53
+ * get importmap cache by cache key
54
+ * @param key cache key
55
+ */
56
+ getCache(key: string): IImportmapData;
57
+ /**
58
+ * set importmap cache
59
+ * @param key cache key
60
+ * @param data importmap data
61
+ */
62
+ setCache(key: string, data: IImportmapData): void;
44
63
  /**
45
64
  * build importmap from deps tree
46
65
  * @param data package data
@@ -8,15 +8,64 @@ 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 utils_1 = require("@umijs/utils");
16
+ const crypto_1 = require("crypto");
17
+ const fs_1 = __importDefault(require("fs"));
18
+ const path_1 = __importDefault(require("path"));
13
19
  /**
14
20
  * class for connect esmi server
15
21
  */
16
22
  class ESMIService {
17
23
  constructor(opts) {
18
24
  this.cdnOrigin = '';
25
+ this.cacheDir = '';
26
+ this.cache = {};
19
27
  this.cdnOrigin = opts.cdnOrigin;
28
+ this.cacheDir = opts.cacheDir;
29
+ // restore local cache
30
+ const cacheFilePath = path_1.default.join(this.cacheDir, 'importmap.json');
31
+ if (fs_1.default.existsSync(cacheFilePath)) {
32
+ try {
33
+ this.cache = require(cacheFilePath);
34
+ }
35
+ catch (_a) {
36
+ /* nothing */
37
+ }
38
+ }
39
+ }
40
+ /**
41
+ * get cache file path by cache key
42
+ * @param data pkg data
43
+ */
44
+ static getCacheKey(data) {
45
+ const hash = (0, crypto_1.createHash)('md4');
46
+ hash.update(JSON.stringify(data.pkgInfo.exports));
47
+ return hash.digest('hex');
48
+ }
49
+ /**
50
+ * get importmap cache by cache key
51
+ * @param key cache key
52
+ */
53
+ getCache(key) {
54
+ return this.cache[key];
55
+ }
56
+ /**
57
+ * set importmap cache
58
+ * @param key cache key
59
+ * @param data importmap data
60
+ */
61
+ setCache(key, data) {
62
+ this.cache[key] = data;
63
+ // create cache dir
64
+ if (!fs_1.default.existsSync(this.cacheDir)) {
65
+ fs_1.default.mkdirSync(this.cacheDir, { recursive: true });
66
+ }
67
+ // write cache to file system
68
+ fs_1.default.writeFileSync(path_1.default.join(this.cacheDir, 'importmap.json'), JSON.stringify(this.cache, null, 2));
20
69
  }
21
70
  /**
22
71
  * build importmap from deps tree
@@ -37,16 +86,35 @@ class ESMIService {
37
86
  */
38
87
  getImportmap(data) {
39
88
  return __awaiter(this, void 0, void 0, function* () {
89
+ const cacheKey = ESMIService.getCacheKey(data);
90
+ const cache = this.getCache(cacheKey);
91
+ const stamp = +new Date();
92
+ // use valid cache first
93
+ if (cache) {
94
+ utils_1.logger.info('ESMi cache used');
95
+ return cache;
96
+ }
97
+ // log dependency list
98
+ utils_1.logger.info(utils_1.chalk.greenBright('Pre-compiling dependencies on esmi:'));
99
+ data.pkgInfo.exports[0].deps.forEach((dep) => {
100
+ console.log(utils_1.chalk.yellow(` ${dep.name}`));
101
+ });
40
102
  // get the build ticket id
41
103
  const ticketId = yield this.build(data);
42
- // continue to the next request after 1s
104
+ // continue to the next request after 2s
43
105
  const next = () => new Promise((resolve) => setTimeout(() => resolve(deferrer()), 2000));
44
106
  const deferrer = () => {
45
107
  return utils_1.axios
46
108
  .get(`${this.cdnOrigin}/api/v1/esm/importmap/${ticketId}`)
47
- .then((res) => (res.data.success ? res.data.data : next()), next);
109
+ .then((res) => {
110
+ if (res.data.success) {
111
+ this.setCache(cacheKey, res.data.data);
112
+ utils_1.logger.info(`Done, took ${((+new Date() - stamp) / 1000).toFixed(1)}s`);
113
+ return res.data.data;
114
+ }
115
+ return next();
116
+ }, next);
48
117
  };
49
- // TODO: timeout + time spend log
50
118
  return deferrer();
51
119
  });
52
120
  }
@@ -109,6 +109,9 @@ exports.default = (api) => {
109
109
  });
110
110
  // skip umi by default
111
111
  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;
112
115
  const data = generatePkgData(api);
113
116
  const deps = data.pkgInfo.exports.reduce((r, exp) => r.concat(exp.deps.map((dep) => dep.name)), []);
114
117
  const hasNewDep = deps.some((i) => !importmap.imports[i]);
@@ -136,7 +139,10 @@ exports.default = (api) => {
136
139
  key: 'esmi',
137
140
  config: {
138
141
  schema(Joi) {
139
- return Joi.object();
142
+ return Joi.object({
143
+ cdnOrigin: Joi.string(),
144
+ shimUrl: Joi.string(),
145
+ });
140
146
  },
141
147
  },
142
148
  enableBy: api.EnableBy.config,
@@ -151,7 +157,10 @@ exports.default = (api) => {
151
157
  api.onBeforeCompiler(() => __awaiter(void 0, void 0, void 0, function* () {
152
158
  if (api.args.vite) {
153
159
  // init esmi service
154
- service = new Service_1.default({ cdnOrigin: api.config.esmi.cdnOrigin });
160
+ service = new Service_1.default({
161
+ cdnOrigin: api.config.esmi.cdnOrigin,
162
+ cacheDir: (0, path_1.join)(api.cwd, '.esmi'),
163
+ });
155
164
  // init project resolver
156
165
  resolver = (0, scan_1.createResolver)({
157
166
  alias: api.config.alias,
@@ -162,10 +171,17 @@ exports.default = (api) => {
162
171
  }));
163
172
  // append ipmortmap script for HTML
164
173
  api.modifyHTML(($) => {
165
- const scp = $('<script type="importmap"></script>');
174
+ const scp = $('<script type="importmap"></script>\n');
166
175
  scp.html(JSON.stringify(importmap, null, 2));
167
176
  $('head > script:eq(0)').before(scp);
168
- // TODO: polyfill for legacy browser
177
+ // append importmap shim script
178
+ if (api.config.esmi.shimUrl) {
179
+ $('body > script:eq(0)').before($(`<script src="${api.config.esmi.shimUrl}"></script>\n`));
180
+ }
181
+ // preload for importmap modules
182
+ Object.values(importmap.imports).forEach((url) => {
183
+ scp.before($(`<link rel="modulepreload" href="${url}" />\n`));
184
+ });
169
185
  return $;
170
186
  });
171
187
  if (api.args.vite) {
@@ -101,10 +101,11 @@ exports.default = (api) => {
101
101
  initialValue: [
102
102
  // TODO: support these methods
103
103
  // 'modifyClientRenderOpts',
104
- // 'patchRoutes',
104
+ 'patchRoutes',
105
105
  'rootContainer',
106
106
  'innerProvider',
107
107
  'i18nProvider',
108
+ 'accessProvider',
108
109
  'dataflowProvider',
109
110
  'outerProvider',
110
111
  // 'render',
package/dist/libs/scan.js CHANGED
@@ -87,6 +87,7 @@ function scan(opts) {
87
87
  const depPath = queueDeps.shift();
88
88
  if (cache.has(depPath))
89
89
  continue;
90
+ // TODO: use parseModule in bundler-utils
90
91
  const content = yield getContent(depPath);
91
92
  const { deps } = yield scanContent({ content });
92
93
  cache.set(depPath, deps);
package/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { RequestHandler } from '@umijs/bundler-webpack';
2
2
  import type webpack from '@umijs/bundler-webpack/compiled/webpack';
3
3
  import type WebpackChain from '@umijs/bundler-webpack/compiled/webpack-5-chain';
4
- import type { InlineConfig as ViteInlineConfig } from 'vite';
5
4
  import type { IAdd, IEvent, IModify, IRoute as ICoreRoute, IServicePluginAPI, PluginAPI } from '@umijs/core';
6
5
  import { Env } from '@umijs/core';
7
6
  import type { CheerioAPI } from '@umijs/utils/compiled/cheerio';
7
+ import type { InlineConfig as ViteInlineConfig } from 'vite';
8
8
  export declare type IScript = Partial<{
9
9
  async: boolean;
10
10
  charset: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umijs/preset-umi",
3
- "version": "4.0.0-beta.12",
3
+ "version": "4.0.0-beta.13",
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,23 +24,23 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@types/multer": "1.4.7",
27
- "@umijs/ast": "4.0.0-beta.12",
28
- "@umijs/babel-preset-umi": "4.0.0-beta.12",
29
- "@umijs/bundler-utils": "4.0.0-beta.12",
30
- "@umijs/bundler-vite": "4.0.0-beta.12",
31
- "@umijs/bundler-webpack": "4.0.0-beta.12",
32
- "@umijs/core": "4.0.0-beta.12",
33
- "@umijs/renderer-react": "4.0.0-beta.12",
34
- "@umijs/server": "4.0.0-beta.12",
35
- "@umijs/utils": "4.0.0-beta.12",
27
+ "@umijs/ast": "4.0.0-beta.13",
28
+ "@umijs/babel-preset-umi": "4.0.0-beta.13",
29
+ "@umijs/bundler-utils": "4.0.0-beta.13",
30
+ "@umijs/bundler-vite": "4.0.0-beta.13",
31
+ "@umijs/bundler-webpack": "4.0.0-beta.13",
32
+ "@umijs/core": "4.0.0-beta.13",
33
+ "@umijs/renderer-react": "4.0.0-beta.13",
34
+ "@umijs/server": "4.0.0-beta.13",
35
+ "@umijs/utils": "4.0.0-beta.13",
36
36
  "current-script-polyfill": "1.0.0",
37
37
  "enhanced-resolve": "5.8.3",
38
38
  "magic-string": "0.25.7",
39
39
  "path-to-regexp": "1.7.0",
40
- "react": "18.0.0-alpha-f2c381131-20211004",
41
- "react-dom": "18.0.0-alpha-f2c381131-20211004",
42
- "react-router": "6.0.2",
43
- "react-router-dom": "6.0.2"
40
+ "react": "18.0.0-rc.0",
41
+ "react-dom": "18.0.0-rc.0",
42
+ "react-router": "6.1.1",
43
+ "react-router-dom": "6.1.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/body-parser": "1.19.2",
@@ -1,6 +1,7 @@
1
1
  {{#plugins}}
2
2
  import * as Plugin_{{{ index }}} from '{{{ path }}}';
3
3
  {{/plugins}}
4
+ import { PluginManager } from 'umi';
4
5
 
5
6
  export function getPlugins() {
6
7
  return [
@@ -16,3 +17,16 @@ export function getPlugins() {
16
17
  export function getValidKeys() {
17
18
  return [{{#validKeys}}'{{{ . }}}',{{/validKeys}}];
18
19
  }
20
+
21
+ let pluginManager = null;
22
+ export function createPluginManager() {
23
+ pluginManager = PluginManager.create({
24
+ plugins: getPlugins(),
25
+ validKeys: getValidKeys(),
26
+ });
27
+ return pluginManager;
28
+ }
29
+
30
+ export function getPluginManager() {
31
+ return pluginManager;
32
+ }
package/templates/umi.tpl CHANGED
@@ -2,17 +2,27 @@
2
2
  {{{ importsAhead }}}
3
3
  import { renderClient } from '{{{ rendererPath }}}';
4
4
  import { getRoutes } from './core/route';
5
- import { getPlugins, getValidKeys } from './core/plugin';
5
+ import { createPluginManager } from './core/plugin';
6
6
  import { PluginManager } from 'umi';
7
7
  {{{ imports }}}
8
8
 
9
9
  async function render() {
10
+ const pluginManager = createPluginManager();
11
+ const { routes, routeComponents } = await getRoutes(pluginManager);
12
+
13
+ // allow user to extend routes
14
+ pluginManager.applyPlugins({
15
+ key: 'patchRoutes',
16
+ type: 'event',
17
+ args: {
18
+ routes,
19
+ routeComponents,
20
+ },
21
+ });
10
22
  const context = {
11
- ...await getRoutes(),
12
- pluginManager: PluginManager.create({
13
- plugins: getPlugins(),
14
- validKeys: getValidKeys(),
15
- }),
23
+ routes,
24
+ routeComponents,
25
+ pluginManager,
16
26
  };
17
27
  return renderClient(context);
18
28
  }