@modern-js/app-tools 2.30.0 → 2.31.0

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 (32) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/cjs/analyze/getHtmlTemplate.js +6 -0
  3. package/dist/cjs/builder/shared/builderPlugins/adapterSSR.js +13 -3
  4. package/dist/cjs/builder/shared/bundlerPlugins/RouterPlugin.js +64 -82
  5. package/dist/cjs/config/default.js +2 -1
  6. package/dist/cjs/schema/index.js +3 -0
  7. package/dist/cjs/types/config/testing.js +4 -0
  8. package/dist/cjs/types/legacyConfig/testing.js +4 -0
  9. package/dist/esm/analyze/getHtmlTemplate.js +8 -1
  10. package/dist/esm/builder/shared/builderPlugins/adapterSSR.js +13 -3
  11. package/dist/esm/builder/shared/bundlerPlugins/RouterPlugin.js +189 -249
  12. package/dist/esm/config/default.js +2 -1
  13. package/dist/esm/schema/index.js +3 -0
  14. package/dist/esm/types/config/testing.js +1 -0
  15. package/dist/esm/types/legacyConfig/testing.js +1 -0
  16. package/dist/esm-node/analyze/getHtmlTemplate.js +6 -0
  17. package/dist/esm-node/builder/shared/builderPlugins/adapterSSR.js +13 -3
  18. package/dist/esm-node/builder/shared/bundlerPlugins/RouterPlugin.js +68 -85
  19. package/dist/esm-node/config/default.js +2 -1
  20. package/dist/esm-node/schema/index.js +3 -0
  21. package/dist/esm-node/types/config/testing.js +1 -0
  22. package/dist/esm-node/types/legacyConfig/testing.js +1 -0
  23. package/dist/types/builder/shared/bundlerPlugins/RouterPlugin.d.ts +24 -0
  24. package/dist/types/types/config/index.d.ts +4 -1
  25. package/dist/types/types/config/output.d.ts +1 -0
  26. package/dist/types/types/config/security.d.ts +3 -1
  27. package/dist/types/types/config/testing.d.ts +8 -0
  28. package/dist/types/types/config/tools.d.ts +1 -1
  29. package/dist/types/types/legacyConfig/index.d.ts +2 -0
  30. package/dist/types/types/legacyConfig/testing.d.ts +8 -0
  31. package/dist/types/types/legacyConfig/tools.d.ts +1 -2
  32. package/package.json +25 -25
@@ -1,9 +1,12 @@
1
- import path from "path";
1
+ import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { createHash } from "crypto";
2
3
  import { mergeWith } from "@modern-js/utils/lodash";
3
4
  import { ROUTE_MANIFEST_FILE } from "@modern-js/utils";
4
5
  import { ROUTE_MANIFEST } from "@modern-js/utils/universal/constants";
5
- import { transform } from "esbuild";
6
6
  const PLUGIN_NAME = "ModernjsRoutePlugin";
7
+ const generateContentHash = (content) => {
8
+ return createHash("md5").update(content).digest("hex").slice(0, 8);
9
+ };
7
10
  export class RouterPlugin {
8
11
  isTargetNodeOrWebWorker(target) {
9
12
  if (target === "node" || Array.isArray(target) && target.includes("node")) {
@@ -37,76 +40,23 @@ export class RouterPlugin {
37
40
  }
38
41
  const { webpack } = compiler;
39
42
  const { Compilation, sources } = webpack;
40
- const { RawSource, SourceMapSource } = sources;
41
- const normalizePath = (path2) => {
42
- if (!path2.endsWith("/")) {
43
- return `${path2}/`;
43
+ const { RawSource } = sources;
44
+ const normalizePath = (path) => {
45
+ if (!path.endsWith("/")) {
46
+ return `${path}/`;
44
47
  }
45
- return path2;
48
+ return path;
46
49
  };
47
- const chunkToSource = /* @__PURE__ */ new Map();
48
- const chunkToMap = /* @__PURE__ */ new Map();
49
- const { minimize } = compiler.options.optimization;
50
+ const chunksToHtmlName = /* @__PURE__ */ new Map();
51
+ const ROUTE_MANIFEST_HOLDER = `route-manifest`;
52
+ const placeholder = `<!--<?- ${ROUTE_MANIFEST_HOLDER} ?>-->`;
50
53
  compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
51
- compilation.hooks.processAssets.tapPromise({
52
- name: PLUGIN_NAME,
53
- stage: Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING
54
- }, async () => {
55
- const stats = compilation.getStats().toJson({
56
- all: false,
57
- chunkGroups: true,
58
- chunks: true,
59
- ids: true
60
- });
61
- const { chunks = [], namedChunkGroups } = stats;
62
- if (!namedChunkGroups) {
63
- return;
64
- }
65
- const entryChunks = this.getEntryChunks(compilation, chunks);
66
- const entryChunkFiles = entryChunks.map((chunk) => [
67
- ...chunk.files || []
68
- ].find((fname) => fname.includes(".js")));
69
- const entryChunkFileIds = entryChunks.map((chunk) => chunk.id);
70
- for (let i = 0; i <= entryChunkFiles.length - 1; i++) {
71
- const file = entryChunkFiles[i];
72
- const chunkId = entryChunkFileIds[i];
73
- const asset = compilation.assets[file];
74
- if (!asset) {
75
- continue;
76
- }
77
- const { map } = asset.sourceAndMap();
78
- chunkToMap.set(chunkId, map);
79
- }
80
- });
81
- compilation.hooks.processAssets.tapPromise({
82
- name: PLUGIN_NAME,
83
- stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
84
- }, async () => {
85
- const stats = compilation.getStats().toJson({
86
- all: false,
87
- chunkGroups: true,
88
- chunks: true,
89
- ids: true
90
- });
91
- const { chunks = [], namedChunkGroups } = stats;
92
- if (!namedChunkGroups) {
93
- return;
94
- }
95
- const entryChunks = this.getEntryChunks(compilation, chunks);
96
- const entryChunkFiles = entryChunks.map((chunk) => [
97
- ...chunk.files || []
98
- ].find((fname) => fname.includes(".js")));
99
- const entryChunkFileIds = entryChunks.map((chunk) => chunk.id);
100
- for (let i = 0; i <= entryChunkFiles.length - 1; i++) {
101
- const file = entryChunkFiles[i];
102
- const chunkId = entryChunkFileIds[i];
103
- const asset = compilation.assets[file];
104
- if (!asset) {
105
- continue;
106
- }
107
- const { source } = asset.sourceAndMap();
108
- chunkToSource.set(chunkId, source);
109
- }
54
+ this.HtmlBundlerPlugin.getHooks(compilation).beforeEmit.tapAsync("RouterManifestPlugin", (data, callback) => {
55
+ const { outputName } = data;
56
+ const { chunks } = data.plugin.options;
57
+ chunksToHtmlName.set(chunks, outputName);
58
+ data.html = data.html.replace("</script>", `</script>${placeholder}`);
59
+ callback(null, data);
110
60
  });
111
61
  compilation.hooks.processAssets.tapPromise({
112
62
  name: PLUGIN_NAME,
@@ -153,11 +103,12 @@ export class RouterPlugin {
153
103
  const manifest = {
154
104
  routeAssets
155
105
  };
106
+ const entryNames = Array.from(compilation.entrypoints.keys());
156
107
  const entryChunks = this.getEntryChunks(compilation, chunks);
157
108
  const entryChunkFiles = this.getEntryChunkFiles(entryChunks);
158
109
  const entryChunkFileIds = entryChunks.map((chunk) => chunk.id);
159
110
  for (let i = 0; i < entryChunkFiles.length; i++) {
160
- var _source, _source1;
111
+ const entryName = entryNames[i];
161
112
  const file = entryChunkFiles[i];
162
113
  const chunkNames = entryChunks[i].names;
163
114
  const chunkId = entryChunkFileIds[i];
@@ -193,21 +144,39 @@ export class RouterPlugin {
193
144
  })};
194
145
  })();
195
146
  `;
196
- const source = chunkToSource.get(chunkId);
197
- const map = chunkToMap.get(chunkId);
198
- const newContent = `${injectedContent}${(_source = source) === null || _source === void 0 ? void 0 : _source.toString()}`;
199
- const result = await transform(newContent, {
200
- loader: path.extname(file).slice(1),
201
- sourcemap: true,
202
- minify: minimize
203
- });
204
- const newSource = new SourceMapSource(result.code, file, result.map, (_source1 = source) === null || _source1 === void 0 ? void 0 : _source1.toString(), map);
205
- compilation.updateAsset(
206
- file,
207
- newSource,
208
- // FIXME: The arguments third of updatgeAsset is a optional function in webpack.
209
- void 0
210
- );
147
+ let htmlName;
148
+ for (const [chunks2, name] of chunksToHtmlName.entries()) {
149
+ if (Array.isArray(chunkNames) && Array.isArray(chunks2) && chunkNames.every((value, index) => value === chunks2[index])) {
150
+ htmlName = name;
151
+ break;
152
+ }
153
+ }
154
+ const oldHtml = compilation.assets[htmlName];
155
+ const { enableInlineRouteManifests, disableFilenameHash, staticJsDir, scriptLoading, nonce } = this;
156
+ const nonceAttr = nonce ? `nonce="${nonce}"` : "";
157
+ if (enableInlineRouteManifests) {
158
+ compilation.updateAsset(
159
+ htmlName,
160
+ new RawSource(oldHtml.source().toString().replace(placeholder, `<script ${nonceAttr}>${injectedContent}</script>`)),
161
+ // FIXME: The arguments third of updatgeAsset is a optional function in webpack.
162
+ void 0
163
+ );
164
+ } else {
165
+ const scriptPath = `${staticJsDir}/${ROUTE_MANIFEST_HOLDER}-${entryName}${disableFilenameHash ? ".js" : `.${generateContentHash(injectedContent)}.js`}`;
166
+ const scriptUrl = `${publicPath}${scriptPath}`;
167
+ const scriptLoadingAttr = (
168
+ // eslint-disable-next-line no-nested-ternary
169
+ scriptLoading === "defer" ? scriptLoading : scriptLoading === "module" ? `type="module"` : ""
170
+ );
171
+ const script = `<script ${scriptLoadingAttr} ${nonceAttr} src="${scriptUrl}"></script>`;
172
+ compilation.updateAsset(
173
+ htmlName,
174
+ new RawSource(oldHtml.source().toString().replace(placeholder, script)),
175
+ // FIXME: The arguments third of updatgeAsset is a optional function in webpack.
176
+ void 0
177
+ );
178
+ compilation.emitAsset(scriptPath, new RawSource(injectedContent));
179
+ }
211
180
  }
212
181
  if (prevManifestAsset) {
213
182
  compilation.updateAsset(
@@ -222,4 +191,18 @@ export class RouterPlugin {
222
191
  });
223
192
  });
224
193
  }
194
+ constructor({ staticJsDir = "static/js", HtmlBundlerPlugin, enableInlineRouteManifests, disableFilenameHash = false, scriptLoading = "defer", nonce }) {
195
+ _define_property(this, "HtmlBundlerPlugin", void 0);
196
+ _define_property(this, "enableInlineRouteManifests", void 0);
197
+ _define_property(this, "staticJsDir", void 0);
198
+ _define_property(this, "disableFilenameHash", void 0);
199
+ _define_property(this, "scriptLoading", void 0);
200
+ _define_property(this, "nonce", void 0);
201
+ this.HtmlBundlerPlugin = HtmlBundlerPlugin;
202
+ this.enableInlineRouteManifests = enableInlineRouteManifests;
203
+ this.staticJsDir = staticJsDir;
204
+ this.disableFilenameHash = disableFilenameHash;
205
+ this.scriptLoading = scriptLoading;
206
+ this.nonce = nonce;
207
+ }
225
208
  }
@@ -11,7 +11,8 @@ export function createDefaultConfig(appContext, bundler) {
11
11
  };
12
12
  const output = {
13
13
  ...defaultBuilderConfig.output,
14
- disableNodePolyfill: true
14
+ disableNodePolyfill: true,
15
+ enableInlineRouteManifests: true
15
16
  };
16
17
  const source = {
17
18
  ...defaultBuilderConfig.source,
@@ -87,6 +87,9 @@ const output = {
87
87
  },
88
88
  disableNodePolyfill: {
89
89
  type: "boolean"
90
+ },
91
+ enableInlineRouteManifests: {
92
+ type: "boolean"
90
93
  }
91
94
  };
92
95
  const dev = {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,7 @@
1
1
  import type { webpack } from '@modern-js/builder-webpack-provider';
2
2
  import type { Rspack } from '@modern-js/builder-rspack-provider';
3
+ import type HtmlWebpackPlugin from '@modern-js/builder-webpack-provider/html-webpack-plugin';
4
+ import type { ScriptLoading } from '@modern-js/builder-shared';
3
5
  export interface RouteAssets {
4
6
  [routeId: string]: {
5
7
  chunkIds?: (string | number)[];
@@ -8,7 +10,29 @@ export interface RouteAssets {
8
10
  };
9
11
  }
10
12
  type Compiler = webpack.Compiler | Rspack.Compiler;
13
+ type Options = {
14
+ HtmlBundlerPlugin: typeof HtmlWebpackPlugin;
15
+ staticJsDir: string;
16
+ enableInlineRouteManifests: boolean;
17
+ disableFilenameHash?: boolean;
18
+ scriptLoading?: ScriptLoading;
19
+ nonce?: string;
20
+ };
11
21
  export declare class RouterPlugin {
22
+ private HtmlBundlerPlugin;
23
+ private enableInlineRouteManifests;
24
+ private staticJsDir;
25
+ private disableFilenameHash?;
26
+ private scriptLoading?;
27
+ private nonce?;
28
+ constructor({
29
+ staticJsDir,
30
+ HtmlBundlerPlugin,
31
+ enableInlineRouteManifests,
32
+ disableFilenameHash,
33
+ scriptLoading,
34
+ nonce
35
+ }: Options);
12
36
  private isTargetNodeOrWebWorker;
13
37
  private getEntryChunks;
14
38
  private getEntryChunkFiles;
@@ -2,10 +2,11 @@ import type { ServerUserConfig, BffUserConfig } from '@modern-js/server-core';
2
2
  import type { BuilderPlugin } from '@modern-js/builder-webpack-provider';
3
3
  import type { OutputUserConfig, RsOutputUserConfig, SharedOutputConfig } from './output';
4
4
  import type { RsSourceUserConfig, SharedSourceConfig, SourceUserConfig } from './source';
5
+ import type { TestingUserConfig } from './testing';
5
6
  import type { DevUserConfig } from './dev';
6
7
  import type { RsToolsUserConfig, SharedToolsConfig, ToolsUserConfig } from './tools';
7
8
  import type { HtmlUserConfig, RsHtmlUserConfig, SharedHtmlConfig } from './html';
8
- import type { RsSecurityConfig, SecurityUserConfig } from './security';
9
+ import type { RsSecurityConfig, SecurityUserConfig, SharedSecurityConfig } from './security';
9
10
  import type { DeployUserConfig } from './deploy';
10
11
  import type { ExperimentsUserConfig } from './experiments';
11
12
  import type { PerformanceUserConfig, RsPerformanceConfig, SharedPerformanceConfig } from './performance';
@@ -32,6 +33,8 @@ export type SharedUserConfig = {
32
33
  runtimeByEntries?: RuntimeByEntriesUserConfig;
33
34
  html?: SharedHtmlConfig;
34
35
  tools?: SharedToolsConfig;
36
+ security?: SharedSecurityConfig;
37
+ testing?: TestingUserConfig;
35
38
  builderPlugins?: BuilderPlugin[];
36
39
  performance?: SharedPerformanceConfig;
37
40
  };
@@ -8,6 +8,7 @@ export interface SharedOutputConfig extends BuilderSharedOutputConfig {
8
8
  ssg?: SSGConfig;
9
9
  splitRouteChunks?: boolean;
10
10
  disableNodePolyfill?: boolean;
11
+ enableInlineRouteManifests?: boolean;
11
12
  tempDir?: string;
12
13
  }
13
14
  export interface OutputUserConfig extends BuilderOutputConfig, SharedOutputConfig {}
@@ -1,5 +1,7 @@
1
+ import type { SharedSecurityConfig } from '@modern-js/builder-shared';
1
2
  import type { WebpackBuilderConfig, RspackBuilderConfig } from '../../builder/shared';
2
3
  import { UnwrapBuilderConfig } from '../utils';
3
4
  export type BuilderSecurityConfig = UnwrapBuilderConfig<WebpackBuilderConfig, 'security'>;
4
5
  export type RsSecurityConfig = UnwrapBuilderConfig<RspackBuilderConfig, 'security'>;
5
- export type SecurityUserConfig = BuilderSecurityConfig;
6
+ export type SecurityUserConfig = BuilderSecurityConfig;
7
+ export type { SharedSecurityConfig };
@@ -0,0 +1,8 @@
1
+ import type { TestConfig } from '@modern-js/types';
2
+ export interface TestingUserConfig {
3
+ /**
4
+ * Decide which transformer will be used to compile file
5
+ * Default: babel-jest
6
+ */
7
+ transformer?: TestConfig['transformer'];
8
+ }
@@ -1,4 +1,4 @@
1
- import type { JestConfig } from '@modern-js/core';
1
+ import type { JestConfig } from '@modern-js/types';
2
2
  import type { PluginSwcOptions } from '@modern-js/builder-plugin-swc';
3
3
  import type { PluginEsbuildOptions } from '@modern-js/builder-plugin-esbuild';
4
4
  import type { SharedToolsConfig as BuilderSharedToolsConfig } from '@modern-js/builder-shared';
@@ -4,6 +4,7 @@ import type { OutputLegacyUserConfig } from './output';
4
4
  import type { SourceLegacyUserConfig } from './source';
5
5
  import type { DeployLegacyUserConfig } from './deploy';
6
6
  import type { ToolsLegacyUserConfig } from './tools';
7
+ import type { TestingLegacyUserConfig } from './testing';
7
8
  export type { DevUserLegacyConfig } from './dev';
8
9
  export type { OutputLegacyUserConfig } from './output';
9
10
  export type { DeployLegacyUserConfig } from './deploy';
@@ -25,6 +26,7 @@ export type AppToolsLegacyUserConfig = {
25
26
  runtime?: RuntimeLegacyConfig;
26
27
  runtimeByEntries?: RuntimeByEntriesLegacyConfig;
27
28
  bff?: LegacyBffUserConfig;
29
+ testing?: TestingLegacyUserConfig;
28
30
  legacy?: boolean;
29
31
  };
30
32
  export interface AppToolsLegacyNormalizedConfig extends Required<AppToolsLegacyUserConfig> {
@@ -0,0 +1,8 @@
1
+ import type { TestConfig } from '@modern-js/types';
2
+ export interface TestingLegacyUserConfig {
3
+ /**
4
+ * Decide which transformer will be used to compile file
5
+ * Default: babel-jest
6
+ */
7
+ transformer?: TestConfig['transformer'];
8
+ }
@@ -1,6 +1,5 @@
1
- import type { DevServerOptions } from '@modern-js/types';
1
+ import type { JestConfig, DevServerOptions } from '@modern-js/types';
2
2
  import type { BuilderConfig } from '@modern-js/builder-webpack-provider';
3
- import type { JestConfig } from '@modern-js/core';
4
3
  type BuilderToolsConfig = Required<BuilderConfig>['tools'];
5
4
  export type ToolsLegacyUserConfig = BuilderToolsConfig & {
6
5
  esbuild?: Record<string, unknown>;
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "modern",
16
16
  "modern.js"
17
17
  ],
18
- "version": "2.30.0",
18
+ "version": "2.31.0",
19
19
  "jsnext:source": "./src/index.ts",
20
20
  "types": "./dist/types/index.d.ts",
21
21
  "main": "./dist/cjs/index.js",
@@ -71,26 +71,26 @@
71
71
  "@babel/types": "^7.21.5",
72
72
  "es-module-lexer": "^1.1.0",
73
73
  "esbuild": "0.17.19",
74
- "rspack-plugin-virtual-module": "0.1.0",
74
+ "rspack-plugin-virtual-module": "0.1.7",
75
75
  "@swc/helpers": "0.5.1",
76
- "@modern-js/builder": "2.30.0",
77
- "@modern-js/builder-plugin-esbuild": "2.30.0",
78
- "@modern-js/builder-plugin-node-polyfill": "2.30.0",
79
- "@modern-js/builder-shared": "2.30.0",
80
- "@modern-js/builder-webpack-provider": "2.30.0",
81
- "@modern-js/core": "2.30.0",
82
- "@modern-js/new-action": "2.30.0",
83
- "@modern-js/node-bundle-require": "2.30.0",
84
- "@modern-js/plugin": "2.30.0",
85
- "@modern-js/plugin-data-loader": "2.30.0",
86
- "@modern-js/plugin-lint": "2.30.0",
87
- "@modern-js/plugin-i18n": "2.30.0",
88
- "@modern-js/server": "2.30.0",
89
- "@modern-js/types": "2.30.0",
90
- "@modern-js/upgrade": "2.30.0",
91
- "@modern-js/server-core": "2.30.0",
92
- "@modern-js/utils": "2.30.0",
93
- "@modern-js/prod-server": "2.30.0"
76
+ "@modern-js/builder": "2.31.0",
77
+ "@modern-js/builder-plugin-esbuild": "2.31.0",
78
+ "@modern-js/builder-plugin-node-polyfill": "2.31.0",
79
+ "@modern-js/builder-webpack-provider": "2.31.0",
80
+ "@modern-js/core": "2.31.0",
81
+ "@modern-js/builder-shared": "2.31.0",
82
+ "@modern-js/new-action": "2.31.0",
83
+ "@modern-js/node-bundle-require": "2.31.0",
84
+ "@modern-js/plugin": "2.31.0",
85
+ "@modern-js/plugin-data-loader": "2.31.0",
86
+ "@modern-js/plugin-i18n": "2.31.0",
87
+ "@modern-js/plugin-lint": "2.31.0",
88
+ "@modern-js/prod-server": "2.31.0",
89
+ "@modern-js/server": "2.31.0",
90
+ "@modern-js/types": "2.31.0",
91
+ "@modern-js/upgrade": "2.31.0",
92
+ "@modern-js/server-core": "2.31.0",
93
+ "@modern-js/utils": "2.31.0"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@types/babel__traverse": "^7.14.2",
@@ -99,13 +99,13 @@
99
99
  "jest": "^29",
100
100
  "typescript": "^5",
101
101
  "webpack": "^5.88.1",
102
- "@modern-js/builder-plugin-swc": "2.30.0",
103
- "@modern-js/builder-rspack-provider": "2.30.0",
104
- "@scripts/build": "2.30.0",
105
- "@scripts/jest-config": "2.30.0"
102
+ "@modern-js/builder-plugin-swc": "2.31.0",
103
+ "@modern-js/builder-rspack-provider": "2.31.0",
104
+ "@scripts/jest-config": "2.31.0",
105
+ "@scripts/build": "2.31.0"
106
106
  },
107
107
  "peerDependencies": {
108
- "@modern-js/builder-rspack-provider": "^2.30.0"
108
+ "@modern-js/builder-rspack-provider": "^2.31.0"
109
109
  },
110
110
  "peerDependenciesMeta": {
111
111
  "@modern-js/builder-rspack-provider": {