@blotch/rsbuild-plugin-angular-lynx 22.2.2 → 22.2.3
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.
- package/README.md +1 -1
- package/dist/544.js +21 -0
- package/dist/angular-rebuild-coordinator.d.ts +1 -1
- package/dist/angular.d.ts +3 -2
- package/dist/background-entry.d.ts +96 -0
- package/dist/client/background-hot-updates.d.ts +50 -0
- package/dist/client/background-hot-updates.js +77 -0
- package/dist/client/background-hot-updates.test.d.ts +1 -0
- package/dist/client/css-hot-update.cjs +9 -0
- package/dist/client/css-hot-update.d.ts +44 -0
- package/dist/client/css-hot-update.js +42 -0
- package/dist/client/css-hot-update.test.d.ts +1 -0
- package/dist/client/hmr-background.js +16 -0
- package/dist/client/hmr-main-thread.js +9 -0
- package/dist/client/hmr-protocol.d.ts +74 -0
- package/dist/client/main-thread-hot-updates.d.ts +40 -0
- package/dist/client/main-thread-hot-updates.js +78 -0
- package/dist/client/main-thread-hot-updates.test.d.ts +1 -0
- package/dist/client/testing/test-context.d.ts +16 -0
- package/dist/component-styles-cache.d.ts +5 -35
- package/dist/component-styles.d.ts +73 -0
- package/dist/component-styles.test.d.ts +1 -0
- package/dist/cross-thread-transform.d.ts +38 -0
- package/dist/cross-thread-transform.test.d.ts +1 -0
- package/dist/css.d.ts +2 -2
- package/dist/entry.d.ts +23 -2
- package/dist/hot-updates.d.ts +67 -0
- package/dist/hot-updates.test.d.ts +1 -0
- package/dist/index.js +718 -168
- package/dist/layers.d.ts +6 -4
- package/dist/logger/dev-logger-middleware.d.ts +13 -7
- package/dist/logger/dev-logger.d.ts +13 -0
- package/dist/polyfills.js +56 -0
- package/dist/polyfills.test.d.ts +1 -0
- package/dist/pure-classes.d.ts +27 -0
- package/dist/pure-classes.test.d.ts +1 -0
- package/dist/resolve.d.ts +19 -0
- package/dist/resolve.test.d.ts +1 -0
- package/dist/split-chunks.d.ts +2 -2
- package/dist/styles/blur-view.css +16 -0
- package/dist/transform-module.d.ts +43 -17
- package/dist/utils/angular/component-scope-id.d.ts +5 -3
- package/dist/utils/options.d.ts +40 -1
- package/package.json +1 -2
- package/dist/loaders/thread-globals-loader.d.ts +0 -8
- package/dist/loaders/thread-globals-loader.js +0 -6
- /package/dist/{loaders/thread-globals-loader.test.d.ts → background-entry.test.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/*! LICENSE: index.js.LICENSE.txt */
|
|
2
2
|
import node_fs, { existsSync } from "node:fs";
|
|
3
|
-
import node_path, { join, resolve } from "node:path";
|
|
3
|
+
import node_path, { join, resolve as external_node_path_resolve } from "node:path";
|
|
4
4
|
import { CSSPlugins, LynxEncodePlugin, LynxTemplatePlugin, WebEncodePlugin } from "@lynx-js/template-webpack-plugin";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
5
7
|
import { RuntimeWrapperWebpackPlugin } from "@lynx-js/runtime-wrapper-webpack-plugin";
|
|
6
8
|
import { RuntimeGlobals } from "@lynx-js/webpack-runtime-globals";
|
|
7
9
|
import tiny_invariant from "tiny-invariant";
|
|
8
10
|
import { createHash } from "node:crypto";
|
|
9
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { createRequire } from "node:module";
|
|
11
11
|
import { availableParallelism } from "node:os";
|
|
12
12
|
import { workspaces } from "@angular-devkit/core";
|
|
13
13
|
import { NodeJsSyncHost } from "@angular-devkit/core/node";
|
|
14
14
|
import { access, constants } from "node:fs/promises";
|
|
15
|
-
import node_http from "node:http";
|
|
16
|
-
import { pluginTailwindCSS } from "rsbuild-plugin-tailwindcss";
|
|
17
15
|
import * as __rspack_external_typescript from "typescript";
|
|
18
16
|
import * as __rspack_external_node_path_c5b9b54f from "node:path";
|
|
19
17
|
const ANGULAR_CLI_CONTEXT = Symbol.for('@blotch/rsbuild-plugin-angular-lynx:angular-cli-context');
|
|
@@ -90,19 +88,32 @@ const LAYERS = {
|
|
|
90
88
|
};
|
|
91
89
|
const applyLayers = (api)=>{
|
|
92
90
|
api.modifyBundlerChain((chain)=>{
|
|
93
|
-
|
|
91
|
+
for (const [layer, target, isMainThread] of [
|
|
92
|
+
[
|
|
93
|
+
LAYERS.BACKGROUND,
|
|
94
|
+
'es2015',
|
|
95
|
+
false
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
LAYERS.MAIN_THREAD,
|
|
99
|
+
'es2019',
|
|
100
|
+
true
|
|
101
|
+
]
|
|
102
|
+
])chain.module.rule("typescript").oneOf(layer).issuerLayer(layer).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
|
|
94
103
|
jsc: {
|
|
95
|
-
target
|
|
96
|
-
parser: {
|
|
97
|
-
syntax: "typescript"
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}).end();
|
|
101
|
-
chain.module.rule("typescript").oneOf(LAYERS.MAIN_THREAD).layer(LAYERS.MAIN_THREAD).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
|
|
102
|
-
jsc: {
|
|
103
|
-
target: 'es2019',
|
|
104
|
+
target,
|
|
104
105
|
parser: {
|
|
105
106
|
syntax: "typescript"
|
|
107
|
+
},
|
|
108
|
+
transform: {
|
|
109
|
+
optimizer: {
|
|
110
|
+
simplify: false,
|
|
111
|
+
globals: {
|
|
112
|
+
vars: {
|
|
113
|
+
__MAIN_THREAD__: String(isMainThread)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
119
|
}).end();
|
|
@@ -201,8 +212,9 @@ class AngularWebpackPlugin {
|
|
|
201
212
|
name: `${this.constructor.name}:threadGlobals`,
|
|
202
213
|
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
203
214
|
}, ()=>{
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
this.#markMainThreadHotUpdates(compilation, mainThreadChunkSet);
|
|
216
|
+
for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js') && !compilation.getAsset(n)?.info.hotModuleReplacement)){
|
|
217
|
+
const isMainThread = mainThreadChunkSet.has(name) || compilation.getAsset(name)?.info['lynx:main-thread'] === true;
|
|
206
218
|
const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
|
|
207
219
|
compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
|
|
208
220
|
}
|
|
@@ -236,6 +248,16 @@ class AngularWebpackPlugin {
|
|
|
236
248
|
});
|
|
237
249
|
});
|
|
238
250
|
}
|
|
251
|
+
#markMainThreadHotUpdates(compilation, mainThreadChunkSet) {
|
|
252
|
+
for (const chunk of compilation.chunks){
|
|
253
|
+
const files = [
|
|
254
|
+
...chunk.files
|
|
255
|
+
];
|
|
256
|
+
if (files.some((file)=>mainThreadChunkSet.has(file) || !compilation.getAsset(file)?.info.hotModuleReplacement && compilation.getAsset(file)?.info['lynx:main-thread'])) {
|
|
257
|
+
for (const file of files)if (file.endsWith('.js') && compilation.getAsset(file)?.info.hotModuleReplacement) this.#updateMainThreadInfo(compilation, file);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
239
261
|
#updateMainThreadInfo(compilation, name) {
|
|
240
262
|
const asset = compilation.getAsset(name);
|
|
241
263
|
tiny_invariant(asset, `Should have main thread asset ${name}`);
|
|
@@ -249,9 +271,17 @@ const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
|
|
|
249
271
|
const PLUGIN_NAME_TEMPLATE = 'lynx:template';
|
|
250
272
|
const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
|
|
251
273
|
const PLUGIN_NAME_ANGULAR = 'lynx:angular';
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
274
|
+
const RULE_BACKGROUND_MODULES = 'lynx:background-modules';
|
|
275
|
+
const PLUGIN_NAME_CSS_HOT_UPDATE = 'lynx:css-hot-update';
|
|
276
|
+
const BLUR_VIEW_STYLESHEET = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './styles/blur-view.css');
|
|
277
|
+
const HMR_BACKGROUND_CLIENT = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/hmr-background.js');
|
|
278
|
+
const HMR_MAIN_THREAD_CLIENT = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/hmr-main-thread.js');
|
|
279
|
+
const CSS_HOT_UPDATE = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/css-hot-update.cjs');
|
|
280
|
+
const CSS_EXTRACT_HOT_UPDATE = /css-extract-webpack-plugin[\\/]runtime[\\/]hotModuleReplacement\.cjs$/;
|
|
281
|
+
const resolveReloadApp = ()=>createRequire(createRequire(import.meta.url).resolve('@lynx-js/rsbuild-plugin')).resolve('@lynx-js/webpack-dev-transport/reloadApp');
|
|
282
|
+
const applyEntry = (api, options, backgroundEntries, appEntries)=>{
|
|
283
|
+
const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, defaultOverflowVisible, enableA11y, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
|
|
284
|
+
api.modifyBundlerChain((chain, { environment, isDev, rspack })=>{
|
|
255
285
|
const lynxConfig = api.useExposed(S_LYNX_CONFIG);
|
|
256
286
|
if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
|
|
257
287
|
const isLynx = 'lynx' === environment.name;
|
|
@@ -261,9 +291,19 @@ const applyEntry = (api, options)=>{
|
|
|
261
291
|
const enabledLiveReload = isDev && !isWeb && false !== liveReload;
|
|
262
292
|
const entries = chain.entryPoints.entries() ?? {};
|
|
263
293
|
chain.entryPoints.clear();
|
|
294
|
+
const preEntry = new Set(environment.config.source.preEntry);
|
|
264
295
|
const mainThreadChunks = [];
|
|
265
296
|
for (const [entryName, entryPoint] of Object.entries(entries)){
|
|
266
|
-
const { imports } = getChunks(entryName, entryPoint.values());
|
|
297
|
+
const { imports: entryImports } = getChunks(entryName, entryPoint.values());
|
|
298
|
+
const mainThreadImports = isLynx ? [
|
|
299
|
+
BLUR_VIEW_STYLESHEET,
|
|
300
|
+
...entryImports
|
|
301
|
+
] : entryImports;
|
|
302
|
+
const backgroundImports = [
|
|
303
|
+
...entryImports.filter((entryImport)=>preEntry.has(entryImport)),
|
|
304
|
+
backgroundEntries.fileFor(environment.name, entryName)
|
|
305
|
+
];
|
|
306
|
+
for (const entryImport of entryImports)if (entryImport.startsWith('.') || node_path.isAbsolute(entryImport)) appEntries.add(node_path.resolve(api.context.rootPath, entryImport));
|
|
267
307
|
const templateFilename = lynxConfig.resolveBundleFilename({
|
|
268
308
|
entryName,
|
|
269
309
|
platform: environment.name
|
|
@@ -278,17 +318,22 @@ const applyEntry = (api, options)=>{
|
|
|
278
318
|
mainThreadChunks.push(mainThreadName);
|
|
279
319
|
chain.entry(mainThreadEntry).add({
|
|
280
320
|
layer: LAYERS.MAIN_THREAD,
|
|
281
|
-
import:
|
|
321
|
+
import: mainThreadImports,
|
|
282
322
|
filename: mainThreadName
|
|
323
|
+
}).when(enabledHMR, (entry)=>{
|
|
324
|
+
entry.prepend({
|
|
325
|
+
layer: LAYERS.MAIN_THREAD,
|
|
326
|
+
import: HMR_MAIN_THREAD_CLIENT
|
|
327
|
+
});
|
|
283
328
|
}).end();
|
|
284
329
|
chain.entry(backgroundEntry).add({
|
|
285
330
|
layer: LAYERS.BACKGROUND,
|
|
286
|
-
import:
|
|
331
|
+
import: backgroundImports,
|
|
287
332
|
filename: backgroundThreadName
|
|
288
333
|
}).when(enabledHMR, (entry)=>{
|
|
289
334
|
entry.prepend({
|
|
290
335
|
layer: LAYERS.BACKGROUND,
|
|
291
|
-
import:
|
|
336
|
+
import: HMR_BACKGROUND_CLIENT
|
|
292
337
|
});
|
|
293
338
|
}).when(enabledHMR || enabledLiveReload, (entry)=>{
|
|
294
339
|
entry.prepend({
|
|
@@ -315,6 +360,7 @@ const applyEntry = (api, options)=>{
|
|
|
315
360
|
customCSSInheritanceList,
|
|
316
361
|
debugInfoOutside,
|
|
317
362
|
defaultDisplayLinear,
|
|
363
|
+
defaultOverflowVisible,
|
|
318
364
|
enableAccessibilityElement,
|
|
319
365
|
enableCSSInheritance,
|
|
320
366
|
enableCSSInvalidation,
|
|
@@ -327,6 +373,14 @@ const applyEntry = (api, options)=>{
|
|
|
327
373
|
}
|
|
328
374
|
]).end();
|
|
329
375
|
}
|
|
376
|
+
chain.module.rule(RULE_BACKGROUND_MODULES).issuerLayer(LAYERS.BACKGROUND).test((file)=>backgroundEntries.isBackgroundModule(file)).sideEffects(true);
|
|
377
|
+
if (enabledHMR) {
|
|
378
|
+
chain.resolve.alias.set('@lynx-js/webpack-dev-transport/reloadApp', resolveReloadApp());
|
|
379
|
+
chain.plugin(PLUGIN_NAME_CSS_HOT_UPDATE).use(rspack.NormalModuleReplacementPlugin, [
|
|
380
|
+
CSS_EXTRACT_HOT_UPDATE,
|
|
381
|
+
CSS_HOT_UPDATE
|
|
382
|
+
]).end();
|
|
383
|
+
}
|
|
330
384
|
if (isLynx) {
|
|
331
385
|
chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
|
|
332
386
|
{
|
|
@@ -622,68 +676,111 @@ const buildLynxSchemaSourceFileCache = (tsconfig, shouldInclude = ()=>true)=>{
|
|
|
622
676
|
fileNames
|
|
623
677
|
};
|
|
624
678
|
};
|
|
625
|
-
const generateComponentScopeId = (className, containingFile)=>{
|
|
626
|
-
const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
|
|
627
|
-
return `l${hash}`;
|
|
628
|
-
};
|
|
629
679
|
const componentStyleSizeKey = (containingFile, stylesheetKey)=>`${containingFile}#${stylesheetKey}`;
|
|
630
680
|
const forgetComponentStyleSizes = (componentStyleSizes, containingFile)=>{
|
|
631
681
|
const prefix = componentStyleSizeKey(containingFile, '');
|
|
632
682
|
for (const key of componentStyleSizes.keys())if (key.startsWith(prefix)) componentStyleSizes.delete(key);
|
|
633
683
|
};
|
|
634
684
|
const createTransformStylesheet = (params)=>{
|
|
635
|
-
const { basePath,
|
|
685
|
+
const { basePath, componentStyleSizes } = params;
|
|
636
686
|
return async (data, containingFile, stylesheetFile, order, className)=>{
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
let componentStyles = componentStylesCache.get(containingFile);
|
|
640
|
-
if (!componentStyles) {
|
|
641
|
-
componentStyles = {
|
|
642
|
-
imports: [],
|
|
643
|
-
inlineStyles: [],
|
|
644
|
-
scopeId,
|
|
645
|
-
processedFiles: new Map()
|
|
646
|
-
};
|
|
647
|
-
componentStylesCache.set(containingFile, componentStyles);
|
|
648
|
-
}
|
|
649
|
-
const stylesheetKey = stylesheetFile ?? `__inline_${order}`;
|
|
650
|
-
const previousPath = componentStyles.processedFiles.get(stylesheetKey);
|
|
651
|
-
if (previousPath && !className) return '';
|
|
652
|
-
componentStyleSizes?.set(componentStyleSizeKey(containingFile, stylesheetKey), {
|
|
687
|
+
const key = componentStyleSizeKey(containingFile, stylesheetFile ?? `__inline_${order}`);
|
|
688
|
+
if (componentStyleSizes && (className || !componentStyleSizes.has(key))) componentStyleSizes.set(key, {
|
|
653
689
|
label: node_path.relative(basePath, stylesheetFile ?? containingFile).replace(/\\/g, '/'),
|
|
654
690
|
size: Buffer.byteLength(data)
|
|
655
691
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
692
|
+
return data;
|
|
693
|
+
};
|
|
694
|
+
};
|
|
695
|
+
const generateComponentScopeId = (className, containingFile)=>{
|
|
696
|
+
const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
|
|
697
|
+
return `l${hash}`;
|
|
698
|
+
};
|
|
699
|
+
const EMULATED = 0;
|
|
700
|
+
const toLynxScopedCss = (css, scopeId)=>css.replaceAll('[_ngcontent-%COMP%]', `._ngcontent-${scopeId}`).replaceAll('[_nghost-%COMP%]', `._nghost-${scopeId}`).replaceAll('%COMP%', scopeId);
|
|
701
|
+
const fillCssVarNamespace = (css)=>css.replaceAll('%NS%', '');
|
|
702
|
+
const propertyName = (property)=>__rspack_external_typescript.isPropertyAssignment(property) && (__rspack_external_typescript.isIdentifier(property.name) || __rspack_external_typescript.isStringLiteral(property.name)) ? property.name.text : null;
|
|
703
|
+
const isDefineComponentCall = (node)=>{
|
|
704
|
+
if (!__rspack_external_typescript.isCallExpression(node)) return false;
|
|
705
|
+
const callee = node.expression;
|
|
706
|
+
const name = __rspack_external_typescript.isPropertyAccessExpression(callee) ? callee.name.text : __rspack_external_typescript.isIdentifier(callee) ? callee.text : null;
|
|
707
|
+
return 'ɵɵdefineComponent' === name;
|
|
708
|
+
};
|
|
709
|
+
const extractComponentStyles = (code, fileName)=>{
|
|
710
|
+
if (!code.includes('ɵɵdefineComponent')) return {
|
|
711
|
+
code,
|
|
712
|
+
components: []
|
|
713
|
+
};
|
|
714
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(fileName, code, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.JS);
|
|
715
|
+
const components = [];
|
|
716
|
+
const removals = [];
|
|
717
|
+
const visit = (node)=>{
|
|
718
|
+
if (isDefineComponentCall(node)) {
|
|
719
|
+
const definition = node.arguments[0];
|
|
720
|
+
if (definition && __rspack_external_typescript.isObjectLiteralExpression(definition)) {
|
|
721
|
+
const properties = definition.properties;
|
|
722
|
+
const find = (name)=>properties.find((property)=>propertyName(property) === name);
|
|
723
|
+
const type = find('type')?.initializer;
|
|
724
|
+
const styles = find('styles');
|
|
725
|
+
const encapsulation = find('encapsulation')?.initializer;
|
|
726
|
+
if (type && __rspack_external_typescript.isIdentifier(type) && styles && __rspack_external_typescript.isArrayLiteralExpression(styles.initializer) && styles.initializer.elements.length > 0 && styles.initializer.elements.every(__rspack_external_typescript.isStringLiteralLike)) {
|
|
727
|
+
const emulated = void 0 === encapsulation || __rspack_external_typescript.isNumericLiteral(encapsulation) && Number(encapsulation.text) === EMULATED;
|
|
728
|
+
const scopeId = emulated ? generateComponentScopeId(type.text, fileName) : void 0;
|
|
729
|
+
const stylesheets = styles.initializer.elements.map((element)=>{
|
|
730
|
+
const css = fillCssVarNamespace(element.text);
|
|
731
|
+
return scopeId ? toLynxScopedCss(css, scopeId) : css;
|
|
732
|
+
});
|
|
733
|
+
components.push({
|
|
734
|
+
className: type.text,
|
|
735
|
+
scopeId,
|
|
736
|
+
stylesheets
|
|
737
|
+
});
|
|
738
|
+
const index = properties.indexOf(styles);
|
|
739
|
+
const next = properties[index + 1];
|
|
740
|
+
removals.push({
|
|
741
|
+
start: styles.getStart(sourceFile),
|
|
742
|
+
end: next ? next.getStart(sourceFile) : properties.hasTrailingComma ? properties.end : styles.end
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
675
746
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
747
|
+
__rspack_external_typescript.forEachChild(node, visit);
|
|
748
|
+
};
|
|
749
|
+
visit(sourceFile);
|
|
750
|
+
let result = code;
|
|
751
|
+
for (const { start, end } of removals.sort((a, b)=>b.start - a.start))result = result.slice(0, start) + result.slice(end);
|
|
752
|
+
return {
|
|
753
|
+
code: result,
|
|
754
|
+
components
|
|
755
|
+
};
|
|
756
|
+
};
|
|
757
|
+
const writeModuleStylesheets = (params)=>{
|
|
758
|
+
const { basePath, cacheDir, fileName, components, onWritten } = params;
|
|
759
|
+
if (0 === components.length) return;
|
|
760
|
+
const prefix = node_path.relative(basePath, fileName).replace(/[/\\]/g, '__').replace(/\.[cm]?[jt]s$/, '');
|
|
761
|
+
const imports = [];
|
|
762
|
+
const scopes = [];
|
|
763
|
+
for (const { className, scopeId, stylesheets } of components){
|
|
764
|
+
stylesheets.forEach((css, index)=>{
|
|
765
|
+
const filePath = node_path.join(cacheDir, `${prefix}.${className}.${index}.css`);
|
|
766
|
+
let current;
|
|
767
|
+
try {
|
|
768
|
+
current = node_fs.readFileSync(filePath, 'utf-8');
|
|
683
769
|
} catch {}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
770
|
+
if (current !== css) {
|
|
771
|
+
node_fs.writeFileSync(filePath, css);
|
|
772
|
+
onWritten?.(filePath);
|
|
773
|
+
}
|
|
774
|
+
imports.push(filePath);
|
|
775
|
+
});
|
|
776
|
+
if (scopeId) scopes.push({
|
|
777
|
+
className,
|
|
778
|
+
scopeId
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
return {
|
|
782
|
+
imports,
|
|
783
|
+
scopes
|
|
687
784
|
};
|
|
688
785
|
};
|
|
689
786
|
const isLynxUnknownElementMessage = (text)=>(text?.includes('is not a known element') || text?.includes("isn't a known property of")) ?? false;
|
|
@@ -723,12 +820,267 @@ const toAngularDiagnosticError = (message, severity)=>{
|
|
|
723
820
|
error.hideStack = true;
|
|
724
821
|
return error;
|
|
725
822
|
};
|
|
823
|
+
const PURE_CONSTRUCTORS = new Set([
|
|
824
|
+
'Map',
|
|
825
|
+
'Set',
|
|
826
|
+
'WeakMap',
|
|
827
|
+
'WeakSet'
|
|
828
|
+
]);
|
|
829
|
+
/**
|
|
830
|
+
* Wraps each top-level class with private or static members in a pure IIFE,
|
|
831
|
+
* so a bundler can drop the classes nothing uses.
|
|
832
|
+
*
|
|
833
|
+
* The background layer compiles to ES2015 (layers.ts), so SWC lowers private
|
|
834
|
+
* and static class fields. A class declaration keeps its own statement, but the
|
|
835
|
+
* class expressions that bundled libraries are written in (`var X = class
|
|
836
|
+
* {…}`) become a sequence that assigns each private field's `WeakMap` to an
|
|
837
|
+
* outer variable: `var X = (_a = new WeakMap(), class {…})`. The assignment
|
|
838
|
+
* counts as a side effect, so rspack keeps every such class, and every import
|
|
839
|
+
* it uses, even when nothing uses the class. The runtime's own classes are
|
|
840
|
+
* like that (it uses `#private` fields throughout), which kept Angular's
|
|
841
|
+
* renderer and change detection in the background bundle.
|
|
842
|
+
*
|
|
843
|
+
* Angular's build already wraps a class whose static members are Angular's
|
|
844
|
+
* own (`ɵcmp`, `ɵfac` and so on) in `/*#__PURE__*\/ (() => {…})()`; this does
|
|
845
|
+
* the same for the rest. Inside the IIFE, the lowering's assignments are local
|
|
846
|
+
* and the call is marked pure. A class whose static members could have side
|
|
847
|
+
* effects of their own (anything but literals, functions and new collections)
|
|
848
|
+
* is left alone.
|
|
849
|
+
*
|
|
850
|
+
* @param code - The module's code, before SWC lowers it (and, for a `.ts`
|
|
851
|
+
* file, strips its types).
|
|
852
|
+
* @param filename - The module's path, for the parser.
|
|
853
|
+
* @returns The transformed code, or `code` itself when nothing applies.
|
|
854
|
+
*/ const wrapPureClasses = (code, filename)=>{
|
|
855
|
+
if (!code.includes('class')) return code;
|
|
856
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
|
|
857
|
+
const edits = [];
|
|
858
|
+
const wrap = (classNode)=>{
|
|
859
|
+
edits.push({
|
|
860
|
+
start: classNode.getStart(sourceFile),
|
|
861
|
+
end: classNode.getStart(sourceFile),
|
|
862
|
+
text: '/*#__PURE__*/ (() => '
|
|
863
|
+
}, {
|
|
864
|
+
start: classNode.end,
|
|
865
|
+
end: classNode.end,
|
|
866
|
+
text: ')()'
|
|
867
|
+
});
|
|
868
|
+
};
|
|
869
|
+
for (const statement of sourceFile.statements){
|
|
870
|
+
if (__rspack_external_typescript.isVariableStatement(statement)) {
|
|
871
|
+
const [declaration, ...rest] = statement.declarationList.declarations;
|
|
872
|
+
const value = declaration?.initializer;
|
|
873
|
+
if (0 === rest.length && value && __rspack_external_typescript.isClassExpression(value) && isLoweredPureClass(value)) wrap(value);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (__rspack_external_typescript.isClassDeclaration(statement) && statement.name && isLoweredPureClass(statement)) {
|
|
877
|
+
const modifiers = __rspack_external_typescript.getModifiers(statement) ?? [];
|
|
878
|
+
const isExport = modifiers.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.ExportKeyword);
|
|
879
|
+
const isDefault = modifiers.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.DefaultKeyword);
|
|
880
|
+
const name = statement.name.text;
|
|
881
|
+
const start = statement.getStart(sourceFile);
|
|
882
|
+
const classStart = statement.getChildren(sourceFile).find((child)=>child.kind === __rspack_external_typescript.SyntaxKind.ClassKeyword).getStart(sourceFile);
|
|
883
|
+
// `class X {}` becomes `let X = /*#__PURE__*/ (() => class X {})();`.
|
|
884
|
+
edits.push({
|
|
885
|
+
start,
|
|
886
|
+
end: classStart,
|
|
887
|
+
text: `${isExport && !isDefault ? 'export ' : ''}let ${name} = /*#__PURE__*/ (() => `
|
|
888
|
+
}, {
|
|
889
|
+
start: statement.end,
|
|
890
|
+
end: statement.end,
|
|
891
|
+
text: `)();${isDefault ? `\nexport { ${name} as default };` : ''}`
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (0 === edits.length) return code;
|
|
896
|
+
edits.sort((a, b)=>b.start - a.start);
|
|
897
|
+
let result = code;
|
|
898
|
+
for (const { start, end, text } of edits)result = result.slice(0, start) + text + result.slice(end);
|
|
899
|
+
return result;
|
|
900
|
+
};
|
|
901
|
+
const isLoweredPureClass = (node)=>{
|
|
902
|
+
if (__rspack_external_typescript.getDecorators(node)?.length) return false;
|
|
903
|
+
const heritage = node.heritageClauses?.find((clause)=>clause.token === __rspack_external_typescript.SyntaxKind.ExtendsKeyword);
|
|
904
|
+
const base = heritage?.types[0]?.expression;
|
|
905
|
+
if (base && !isPureReference(base)) return false;
|
|
906
|
+
let lowered = false;
|
|
907
|
+
for (const member of node.members){
|
|
908
|
+
if (__rspack_external_typescript.isClassStaticBlockDeclaration(member)) return false;
|
|
909
|
+
const isStatic = __rspack_external_typescript.getModifiers(member)?.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.StaticKeyword);
|
|
910
|
+
const isPrivate = member.name && __rspack_external_typescript.isPrivateIdentifier(member.name);
|
|
911
|
+
if (member.name && __rspack_external_typescript.isComputedPropertyName(member.name)) return false;
|
|
912
|
+
if (isStatic || isPrivate) lowered = true;
|
|
913
|
+
if (isStatic && __rspack_external_typescript.isPropertyDeclaration(member) && member.initializer && !isPure(member.initializer)) return false;
|
|
914
|
+
}
|
|
915
|
+
return lowered;
|
|
916
|
+
};
|
|
917
|
+
const isPureReference = (node)=>__rspack_external_typescript.isIdentifier(node) || __rspack_external_typescript.isPropertyAccessExpression(node) && isPureReference(node.expression);
|
|
918
|
+
const isPure = (node)=>{
|
|
919
|
+
if (__rspack_external_typescript.isLiteralExpression(node) || __rspack_external_typescript.isNoSubstitutionTemplateLiteral(node) || __rspack_external_typescript.isIdentifier(node) || __rspack_external_typescript.isArrowFunction(node) || __rspack_external_typescript.isFunctionExpression(node) || node.kind === __rspack_external_typescript.SyntaxKind.TrueKeyword || node.kind === __rspack_external_typescript.SyntaxKind.FalseKeyword || node.kind === __rspack_external_typescript.SyntaxKind.NullKeyword) return true;
|
|
920
|
+
if (__rspack_external_typescript.isVoidExpression(node)) return isPure(node.expression);
|
|
921
|
+
if (__rspack_external_typescript.isParenthesizedExpression(node)) return isPure(node.expression);
|
|
922
|
+
if (__rspack_external_typescript.isPrefixUnaryExpression(node)) return isPure(node.operand);
|
|
923
|
+
if (__rspack_external_typescript.isArrayLiteralExpression(node)) return node.elements.every((element)=>!__rspack_external_typescript.isSpreadElement(element) && isPure(element));
|
|
924
|
+
if (__rspack_external_typescript.isObjectLiteralExpression(node)) return node.properties.every((property)=>__rspack_external_typescript.isPropertyAssignment(property) && !__rspack_external_typescript.isComputedPropertyName(property.name) && isPure(property.initializer));
|
|
925
|
+
return __rspack_external_typescript.isNewExpression(node) && __rspack_external_typescript.isIdentifier(node.expression) && PURE_CONSTRUCTORS.has(node.expression.text) && (node.arguments ?? []).every(isPure);
|
|
926
|
+
};
|
|
726
927
|
const seedCompilationSourceFiles = (compilation, sourceFileCache)=>{
|
|
727
928
|
const { sourceFiles } = compilation;
|
|
728
929
|
if (!(sourceFiles instanceof Map)) return false;
|
|
729
930
|
for (const [fileName, sourceFile] of sourceFileCache)sourceFiles.set(fileName, sourceFile);
|
|
730
931
|
return true;
|
|
731
932
|
};
|
|
933
|
+
const IN_PLACE_FIELDS = new Set([
|
|
934
|
+
'template',
|
|
935
|
+
'templateUrl',
|
|
936
|
+
'styles',
|
|
937
|
+
'styleUrl',
|
|
938
|
+
'styleUrls'
|
|
939
|
+
]);
|
|
940
|
+
const hotUpdatesEnabled = (environment)=>'web' !== environment.name && 'development' === environment.config.mode && environment.config.dev?.hmr !== false;
|
|
941
|
+
const hot_updates_hash = (text)=>createHash('sha1').update(text).digest('hex').slice(0, 16);
|
|
942
|
+
const isComponentDecorator = (decorator)=>{
|
|
943
|
+
const call = decorator.expression;
|
|
944
|
+
if (!__rspack_external_typescript.isCallExpression(call)) return false;
|
|
945
|
+
const callee = call.expression;
|
|
946
|
+
const name = __rspack_external_typescript.isPropertyAccessExpression(callee) ? callee.name.text : __rspack_external_typescript.isIdentifier(callee) ? callee.text : void 0;
|
|
947
|
+
return 'Component' === name;
|
|
948
|
+
};
|
|
949
|
+
const hotUpdateSignature = (source, fileName)=>{
|
|
950
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(fileName, source, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.TS);
|
|
951
|
+
const removals = [];
|
|
952
|
+
const visit = (node)=>{
|
|
953
|
+
if (__rspack_external_typescript.isDecorator(node) && isComponentDecorator(node)) {
|
|
954
|
+
const metadata = node.expression.arguments[0];
|
|
955
|
+
if (metadata && __rspack_external_typescript.isObjectLiteralExpression(metadata)) {
|
|
956
|
+
for (const property of metadata.properties)if (__rspack_external_typescript.isPropertyAssignment(property) && (__rspack_external_typescript.isIdentifier(property.name) || __rspack_external_typescript.isStringLiteral(property.name)) && IN_PLACE_FIELDS.has(property.name.text)) removals.push({
|
|
957
|
+
start: property.initializer.getStart(sourceFile),
|
|
958
|
+
end: property.initializer.end
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
__rspack_external_typescript.forEachChild(node, visit);
|
|
963
|
+
};
|
|
964
|
+
visit(sourceFile);
|
|
965
|
+
let rest = source;
|
|
966
|
+
for (const { start, end } of removals.reverse())rest = rest.slice(0, start) + rest.slice(end);
|
|
967
|
+
return hot_updates_hash(rest);
|
|
968
|
+
};
|
|
969
|
+
const componentClassNames = (code, fileName)=>{
|
|
970
|
+
if (!code.includes('ɵɵdefineComponent')) return [];
|
|
971
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(fileName, code, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.JS);
|
|
972
|
+
const topLevelClasses = new Set(sourceFile.statements.filter(__rspack_external_typescript.isClassDeclaration).flatMap((statement)=>statement.name ? [
|
|
973
|
+
statement.name.text
|
|
974
|
+
] : []));
|
|
975
|
+
const names = [];
|
|
976
|
+
const visit = (node)=>{
|
|
977
|
+
if (__rspack_external_typescript.isCallExpression(node) && (__rspack_external_typescript.isPropertyAccessExpression(node.expression) ? node.expression.name.text : __rspack_external_typescript.isIdentifier(node.expression) ? node.expression.text : void 0) === 'ɵɵdefineComponent') {
|
|
978
|
+
const definition = node.arguments[0];
|
|
979
|
+
const type = definition && __rspack_external_typescript.isObjectLiteralExpression(definition) ? definition.properties.find((property)=>__rspack_external_typescript.isPropertyAssignment(property) && __rspack_external_typescript.isIdentifier(property.name) && 'type' === property.name.text)?.initializer : void 0;
|
|
980
|
+
if (type && __rspack_external_typescript.isIdentifier(type) && topLevelClasses.has(type.text) && !names.includes(type.text)) names.push(type.text);
|
|
981
|
+
}
|
|
982
|
+
__rspack_external_typescript.forEachChild(node, visit);
|
|
983
|
+
};
|
|
984
|
+
visit(sourceFile);
|
|
985
|
+
return names;
|
|
986
|
+
};
|
|
987
|
+
const ACCEPT = 'ɵɵangularLynxHmrAccept';
|
|
988
|
+
const addHotAccept = (code, { components, signature, compiled })=>{
|
|
989
|
+
const list = `[${components.join(', ')}]`;
|
|
990
|
+
return `${code}
|
|
991
|
+
import { ɵhmrAccept as ${ACCEPT} } from '@blotch/angular-lynx';
|
|
992
|
+
if (import.meta.webpackHot) {
|
|
993
|
+
${list} = ${ACCEPT}(import.meta.webpackHot, ${list}, ${JSON.stringify(signature)}, ${JSON.stringify(hot_updates_hash(compiled))});
|
|
994
|
+
}
|
|
995
|
+
`;
|
|
996
|
+
};
|
|
997
|
+
const addEntryHotAccept = (code)=>`${code}
|
|
998
|
+
if (import.meta.webpackHot) {
|
|
999
|
+
import.meta.webpackHot.accept();
|
|
1000
|
+
}
|
|
1001
|
+
`;
|
|
1002
|
+
const RUNTIME_MODULE = '@blotch/angular-lynx';
|
|
1003
|
+
const TAGGED_CALLS = new Set([
|
|
1004
|
+
'backgroundFn',
|
|
1005
|
+
'createMainThreadRef'
|
|
1006
|
+
]);
|
|
1007
|
+
const TAGGED_CLASSES = new Set([
|
|
1008
|
+
'MainThreadRef'
|
|
1009
|
+
]);
|
|
1010
|
+
const transformCrossThread = (code, filename, options)=>{
|
|
1011
|
+
const calls = findModuleLevelCalls(code, filename);
|
|
1012
|
+
if (0 === calls.length) return code;
|
|
1013
|
+
const fileKey = keyForFile(filename, options.root);
|
|
1014
|
+
const edits = [];
|
|
1015
|
+
calls.forEach((node, index)=>{
|
|
1016
|
+
const edit = tagEdit(node, JSON.stringify(`${fileKey}:${index}`), code);
|
|
1017
|
+
if (edit) edits.push(edit);
|
|
1018
|
+
});
|
|
1019
|
+
edits.sort((a, b)=>b.start - a.start);
|
|
1020
|
+
let result = code;
|
|
1021
|
+
for (const { start, end, text } of edits)result = result.slice(0, start) + text + result.slice(end);
|
|
1022
|
+
return result;
|
|
1023
|
+
};
|
|
1024
|
+
const definesCrossThreadValues = (code, filename)=>findModuleLevelCalls(code, filename).length > 0;
|
|
1025
|
+
const findModuleLevelCalls = (code, filename)=>{
|
|
1026
|
+
if (!code.includes(RUNTIME_MODULE) || !code.includes('backgroundFn') && !code.includes('MainThreadRef')) return [];
|
|
1027
|
+
const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
|
|
1028
|
+
const runtime = runtimeBindings(sourceFile);
|
|
1029
|
+
const calls = [];
|
|
1030
|
+
const visit = (node, topLevel)=>{
|
|
1031
|
+
if (topLevel && (__rspack_external_typescript.isCallExpression(node) && isRuntimeReference(node.expression, runtime, TAGGED_CALLS) || __rspack_external_typescript.isNewExpression(node) && isRuntimeReference(node.expression, runtime, TAGGED_CLASSES))) calls.push(node);
|
|
1032
|
+
const childTopLevel = topLevel && !__rspack_external_typescript.isFunctionLike(node) && !__rspack_external_typescript.isClassLike(node);
|
|
1033
|
+
__rspack_external_typescript.forEachChild(node, (child)=>visit(child, childTopLevel));
|
|
1034
|
+
};
|
|
1035
|
+
visit(sourceFile, true);
|
|
1036
|
+
return calls;
|
|
1037
|
+
};
|
|
1038
|
+
const tagEdit = (node, id, code)=>{
|
|
1039
|
+
const args = node.arguments ?? __rspack_external_typescript.factory.createNodeArray();
|
|
1040
|
+
if (args.length > 1 || args.some(__rspack_external_typescript.isSpreadElement)) return;
|
|
1041
|
+
if (1 === args.length) return {
|
|
1042
|
+
start: args[0].end,
|
|
1043
|
+
end: args[0].end,
|
|
1044
|
+
text: `, ${id}`
|
|
1045
|
+
};
|
|
1046
|
+
const close = node.getEnd() - 1;
|
|
1047
|
+
if (')' !== code[close]) return;
|
|
1048
|
+
return {
|
|
1049
|
+
start: close,
|
|
1050
|
+
end: close,
|
|
1051
|
+
text: `undefined, ${id}`
|
|
1052
|
+
};
|
|
1053
|
+
};
|
|
1054
|
+
const runtimeBindings = (sourceFile)=>{
|
|
1055
|
+
const named = new Map();
|
|
1056
|
+
const namespaces = new Set();
|
|
1057
|
+
for (const statement of sourceFile.statements){
|
|
1058
|
+
if (!__rspack_external_typescript.isImportDeclaration(statement) || !__rspack_external_typescript.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== RUNTIME_MODULE) continue;
|
|
1059
|
+
const bindings = statement.importClause?.namedBindings;
|
|
1060
|
+
if (bindings) {
|
|
1061
|
+
if (__rspack_external_typescript.isNamespaceImport(bindings)) {
|
|
1062
|
+
namespaces.add(bindings.name.text);
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
for (const element of bindings.elements)named.set(element.name.text, (element.propertyName ?? element.name).text);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return {
|
|
1069
|
+
named,
|
|
1070
|
+
namespaces
|
|
1071
|
+
};
|
|
1072
|
+
};
|
|
1073
|
+
const isRuntimeReference = (expression, runtime, exports)=>{
|
|
1074
|
+
if (__rspack_external_typescript.isIdentifier(expression)) {
|
|
1075
|
+
const imported = runtime.named.get(expression.text);
|
|
1076
|
+
return void 0 !== imported && exports.has(imported);
|
|
1077
|
+
}
|
|
1078
|
+
return __rspack_external_typescript.isPropertyAccessExpression(expression) && __rspack_external_typescript.isIdentifier(expression.expression) && runtime.namespaces.has(expression.expression.text) && exports.has(expression.name.text);
|
|
1079
|
+
};
|
|
1080
|
+
const keyForFile = (filename, root)=>{
|
|
1081
|
+
const relative = node_path.relative(root, filename).split(node_path.sep).join('/');
|
|
1082
|
+
return createHash('sha1').update(relative).digest('hex').slice(0, 10);
|
|
1083
|
+
};
|
|
732
1084
|
const MAIN_THREAD_DIRECTIVE = 'main thread';
|
|
733
1085
|
const MAIN_THREAD_DIRECTIVE_ALT = 'main-thread';
|
|
734
1086
|
const transformWorklets = (code, filename)=>{
|
|
@@ -775,10 +1127,10 @@ const isAlreadyWrapped = (node)=>{
|
|
|
775
1127
|
return false;
|
|
776
1128
|
};
|
|
777
1129
|
const buildTransformedCode = (params)=>{
|
|
778
|
-
const { resourcePath,
|
|
1130
|
+
const { resourcePath, stylesheets, root, hot } = params;
|
|
779
1131
|
let code = params.code;
|
|
780
|
-
if (
|
|
781
|
-
const { imports } =
|
|
1132
|
+
if (stylesheets) {
|
|
1133
|
+
const { imports } = stylesheets;
|
|
782
1134
|
let importsString = '';
|
|
783
1135
|
for(let i = 0; i < imports.length; ++i){
|
|
784
1136
|
let relativeImport = node_path.relative(node_path.dirname(resourcePath), imports[i]);
|
|
@@ -787,8 +1139,19 @@ const buildTransformedCode = (params)=>{
|
|
|
787
1139
|
}
|
|
788
1140
|
code = importsString + code;
|
|
789
1141
|
}
|
|
790
|
-
|
|
791
|
-
|
|
1142
|
+
for (const { className, scopeId } of stylesheets?.scopes ?? [])code += `\n;${className}.ɵcmp.id = '${scopeId}';\n`;
|
|
1143
|
+
const transformed = transformCrossThread(transformWorklets(code, resourcePath), resourcePath, {
|
|
1144
|
+
root
|
|
1145
|
+
});
|
|
1146
|
+
if (!hot) return transformed;
|
|
1147
|
+
if (hot.entry) return addEntryHotAccept(transformed);
|
|
1148
|
+
const components = componentClassNames(params.code, resourcePath);
|
|
1149
|
+
if (0 === components.length) return transformed;
|
|
1150
|
+
return addHotAccept(transformed, {
|
|
1151
|
+
components,
|
|
1152
|
+
signature: hotUpdateSignature(hot.source(), resourcePath),
|
|
1153
|
+
compiled: params.code
|
|
1154
|
+
});
|
|
792
1155
|
};
|
|
793
1156
|
const applyAngularConfig = (api, buildOptions)=>{
|
|
794
1157
|
const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -805,9 +1168,9 @@ const applyAngularConfig = (api, buildOptions)=>{
|
|
|
805
1168
|
];
|
|
806
1169
|
else config.source.preEntry ??= [];
|
|
807
1170
|
config.source.preEntry.push(node_path.resolve(__dirname, './polyfills'));
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1171
|
+
const polyfills = buildOptions.polyfills ?? [];
|
|
1172
|
+
if (buildOptions.i18n.hasDefinedSourceLocale && !polyfills.some((polyfill)=>polyfill.startsWith('@angular/localize'))) config.source.preEntry.push('@angular/localize/init');
|
|
1173
|
+
config.source.preEntry.push(...polyfills);
|
|
811
1174
|
config.source.preEntry.push(...buildOptions.styles);
|
|
812
1175
|
config.source.tsconfigPath = buildOptions.tsconfig;
|
|
813
1176
|
config.source.define ??= {};
|
|
@@ -916,7 +1279,7 @@ const normalizeCacheOptions = (projectMetadata, workspaceRoot)=>{
|
|
|
916
1279
|
cacheEnabled = !isCI;
|
|
917
1280
|
break;
|
|
918
1281
|
}
|
|
919
|
-
const cacheBasePath =
|
|
1282
|
+
const cacheBasePath = external_node_path_resolve(workspaceRoot, path);
|
|
920
1283
|
return {
|
|
921
1284
|
enabled: cacheEnabled,
|
|
922
1285
|
basePath: cacheBasePath,
|
|
@@ -1325,7 +1688,7 @@ const reportLynxDiagnostics = (diagnostics)=>{
|
|
|
1325
1688
|
for (const w of cssWarnings)console.warn(` ${w.message}`);
|
|
1326
1689
|
}
|
|
1327
1690
|
};
|
|
1328
|
-
const applyAngularRules = async (api, pluginOptions)=>{
|
|
1691
|
+
const applyAngularRules = async (api, pluginOptions, backgroundEntries, appEntries)=>{
|
|
1329
1692
|
const cli = getAngularCliContext(api);
|
|
1330
1693
|
const { basePath, workspace } = await getAngularWorkspace(cli?.workspaceRoot);
|
|
1331
1694
|
if (pluginOptions.pages) {
|
|
@@ -1365,25 +1728,22 @@ const applyAngularRules = async (api, pluginOptions)=>{
|
|
|
1365
1728
|
const tsconfig = buildOptions.tsconfig;
|
|
1366
1729
|
const compilation = await createAngularCompilation(false, aot);
|
|
1367
1730
|
const typeScriptFileCache = new Map();
|
|
1368
|
-
const
|
|
1369
|
-
node_fs.mkdirSync(
|
|
1731
|
+
const componentCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
|
|
1732
|
+
node_fs.mkdirSync(componentCssCacheDir, {
|
|
1370
1733
|
recursive: true
|
|
1371
1734
|
});
|
|
1372
|
-
const
|
|
1373
|
-
const componentScopeIds = new Map();
|
|
1735
|
+
const moduleStylesheets = new Map();
|
|
1374
1736
|
let componentResourceDependencies = new Map();
|
|
1375
1737
|
const writtenStylesheets = new Set();
|
|
1376
1738
|
let knownSourceFiles = new Set();
|
|
1739
|
+
const backgroundModules = new Set();
|
|
1740
|
+
let angularInputs = new Set();
|
|
1377
1741
|
let angularDiagnostics = {
|
|
1378
1742
|
errors: [],
|
|
1379
1743
|
warnings: []
|
|
1380
1744
|
};
|
|
1381
1745
|
const transformStylesheet = createTransformStylesheet({
|
|
1382
1746
|
basePath,
|
|
1383
|
-
scopedCssCacheDir,
|
|
1384
|
-
componentStylesCache,
|
|
1385
|
-
componentScopeIds,
|
|
1386
|
-
onStylesheetWritten: (filePath)=>writtenStylesheets.add(filePath),
|
|
1387
1747
|
componentStyleSizes
|
|
1388
1748
|
});
|
|
1389
1749
|
const compileAngular = async (changedFiles)=>{
|
|
@@ -1420,8 +1780,22 @@ const applyAngularRules = async (api, pluginOptions)=>{
|
|
|
1420
1780
|
try {
|
|
1421
1781
|
for (const { filename, contents } of (await compilation.emitAffectedFiles())){
|
|
1422
1782
|
const file = node_path.normalize(filename);
|
|
1423
|
-
|
|
1424
|
-
|
|
1783
|
+
const { code, components } = extractComponentStyles('string' == typeof contents ? contents : Buffer.from(contents).toString(), file);
|
|
1784
|
+
const stylesheets = writeModuleStylesheets({
|
|
1785
|
+
basePath,
|
|
1786
|
+
cacheDir: componentCssCacheDir,
|
|
1787
|
+
fileName: file,
|
|
1788
|
+
components,
|
|
1789
|
+
onWritten: (filePath)=>writtenStylesheets.add(filePath)
|
|
1790
|
+
});
|
|
1791
|
+
if (stylesheets) moduleStylesheets.set(file, stylesheets);
|
|
1792
|
+
else moduleStylesheets.delete(file);
|
|
1793
|
+
if (typeScriptFileCache.get(file) !== code) {
|
|
1794
|
+
changedOutputs.add(file);
|
|
1795
|
+
if (definesCrossThreadValues(code, file)) backgroundModules.add(file);
|
|
1796
|
+
else backgroundModules.delete(file);
|
|
1797
|
+
}
|
|
1798
|
+
typeScriptFileCache.set(file, code);
|
|
1425
1799
|
}
|
|
1426
1800
|
} catch (error) {
|
|
1427
1801
|
throw new Error(`Angular failed to emit compiled output for "${tsconfig}". Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
@@ -1429,6 +1803,8 @@ const applyAngularRules = async (api, pluginOptions)=>{
|
|
|
1429
1803
|
for (const file of writtenStylesheets)changedOutputs.add(file);
|
|
1430
1804
|
for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) {
|
|
1431
1805
|
typeScriptFileCache.delete(file);
|
|
1806
|
+
backgroundModules.delete(file);
|
|
1807
|
+
moduleStylesheets.delete(file);
|
|
1432
1808
|
forgetComponentStyleSizes(componentStyleSizes, file);
|
|
1433
1809
|
}
|
|
1434
1810
|
reportLynxDiagnostics([
|
|
@@ -1438,9 +1814,16 @@ const applyAngularRules = async (api, pluginOptions)=>{
|
|
|
1438
1814
|
...scanSourcesForUnsupportedCss(fileNames)
|
|
1439
1815
|
]);
|
|
1440
1816
|
angularDiagnostics = filterAngularDiagnostics(await compilation.diagnoseFiles(useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic));
|
|
1817
|
+
const inputFiles = result.referencedFiles.map((file)=>node_path.normalize(file));
|
|
1818
|
+
angularInputs = new Set([
|
|
1819
|
+
...inputFiles,
|
|
1820
|
+
...[
|
|
1821
|
+
...componentResourceDependencies.values()
|
|
1822
|
+
].flat()
|
|
1823
|
+
]);
|
|
1441
1824
|
return {
|
|
1442
1825
|
changedOutputs,
|
|
1443
|
-
inputFiles
|
|
1826
|
+
inputFiles
|
|
1444
1827
|
};
|
|
1445
1828
|
};
|
|
1446
1829
|
const coordinator = createAngularRebuildCoordinator({
|
|
@@ -1460,46 +1843,199 @@ const applyAngularRules = async (api, pluginOptions)=>{
|
|
|
1460
1843
|
let compilationClosed = false;
|
|
1461
1844
|
api.onBeforeEnvironmentCompile(async ({ environment, isWatch })=>{
|
|
1462
1845
|
const compiler = compilersByEnvironment.get(environment.name);
|
|
1463
|
-
const
|
|
1846
|
+
const changedFiles = [
|
|
1464
1847
|
...compiler?.modifiedFiles ?? [],
|
|
1465
1848
|
...compiler?.removedFiles ?? []
|
|
1466
|
-
]
|
|
1467
|
-
|
|
1849
|
+
];
|
|
1850
|
+
const outdatedFiles = await coordinator.prepare(environment.name, changedFiles);
|
|
1851
|
+
const changedEntries = backgroundEntries.update(environment.name, {
|
|
1852
|
+
appModules: backgroundModules,
|
|
1853
|
+
watch: isWatch,
|
|
1854
|
+
rebuilt: outdatedFiles.size > 0 || changedFiles.some((file)=>!angularInputs.has(node_path.normalize(file)) && !backgroundEntries.isEntryFile(file))
|
|
1855
|
+
});
|
|
1856
|
+
if (compiler && outdatedFiles.size + changedEntries.length > 0) compiler.modifiedFiles = new Set([
|
|
1468
1857
|
...compiler.modifiedFiles ?? [],
|
|
1469
|
-
...outdatedFiles
|
|
1858
|
+
...outdatedFiles,
|
|
1859
|
+
...changedEntries
|
|
1470
1860
|
]);
|
|
1471
1861
|
if (!isWatch && !compilationClosed) {
|
|
1472
1862
|
compilationClosed = true;
|
|
1473
1863
|
await compilation.close?.();
|
|
1474
1864
|
}
|
|
1475
1865
|
});
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}, async (context)=>{
|
|
1866
|
+
const transformModule = async (context, background)=>{
|
|
1867
|
+
const hot = hotUpdatesEnabled(context.environment);
|
|
1479
1868
|
const isJs = /\.[cm]?js$/.test(context.resourcePath);
|
|
1480
1869
|
if (isJs) {
|
|
1481
1870
|
const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
|
|
1482
1871
|
skipLinker: false,
|
|
1483
1872
|
sideEffects: ()=>Promise.resolve(false)
|
|
1484
1873
|
});
|
|
1874
|
+
const code = transformCrossThread(transformWorklets(Buffer.from(contents).toString(), context.resourcePath), context.resourcePath, {
|
|
1875
|
+
root: api.context.rootPath
|
|
1876
|
+
});
|
|
1877
|
+
if (!background) return {
|
|
1878
|
+
code
|
|
1879
|
+
};
|
|
1880
|
+
const wrapped = wrapPureClasses(code, context.resourcePath);
|
|
1485
1881
|
return {
|
|
1486
|
-
code:
|
|
1882
|
+
code: hot && backgroundEntries.isEntryFile(context.resourcePath) ? addEntryHotAccept(wrapped) : wrapped
|
|
1487
1883
|
};
|
|
1488
1884
|
}
|
|
1489
1885
|
const content = typeScriptFileCache.get(context.resourcePath);
|
|
1490
1886
|
if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
|
|
1491
1887
|
for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
|
|
1492
|
-
const code =
|
|
1888
|
+
const code = buildTransformedCode({
|
|
1889
|
+
code: 'string' == typeof content ? content : Buffer.from(content).toString(),
|
|
1890
|
+
resourcePath: context.resourcePath,
|
|
1891
|
+
stylesheets: moduleStylesheets.get(context.resourcePath),
|
|
1892
|
+
root: api.context.rootPath,
|
|
1893
|
+
hot: hot && !background ? {
|
|
1894
|
+
entry: appEntries.has(node_path.normalize(context.resourcePath)),
|
|
1895
|
+
source: ()=>node_fs.readFileSync(context.resourcePath, 'utf-8')
|
|
1896
|
+
} : void 0
|
|
1897
|
+
});
|
|
1898
|
+
if (!background) return {
|
|
1899
|
+
code
|
|
1900
|
+
};
|
|
1493
1901
|
return {
|
|
1494
|
-
code:
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
scopeInfo: componentScopeIds.get(context.resourcePath)
|
|
1499
|
-
})
|
|
1902
|
+
code: wrapPureClasses(Buffer.from(await javascriptTransformer.transformData(context.resourcePath, code, {
|
|
1903
|
+
skipLinker: true,
|
|
1904
|
+
sideEffects: ()=>Promise.resolve(false)
|
|
1905
|
+
})).toString(), context.resourcePath)
|
|
1500
1906
|
};
|
|
1907
|
+
};
|
|
1908
|
+
api.transform({
|
|
1909
|
+
test: /\.[cm]?[jt]sx?$/,
|
|
1910
|
+
issuerLayer: LAYERS.MAIN_THREAD
|
|
1911
|
+
}, (context)=>transformModule(context, false));
|
|
1912
|
+
api.transform({
|
|
1913
|
+
test: /\.[cm]?[jt]sx?$/,
|
|
1914
|
+
issuerLayer: LAYERS.BACKGROUND
|
|
1915
|
+
}, (context)=>transformModule(context, true));
|
|
1916
|
+
};
|
|
1917
|
+
const background_entry_RUNTIME_MODULE = '@blotch/angular-lynx';
|
|
1918
|
+
const createBackgroundEntries = ({ rootPath, findLibraryModules = findLibraryBackgroundModules })=>{
|
|
1919
|
+
const cacheDir = node_path.join(rootPath, 'node_modules', '.cache', 'angular-lynx', 'background-entries');
|
|
1920
|
+
const files = new Map();
|
|
1921
|
+
let libraryModules;
|
|
1922
|
+
const getLibraryModules = ()=>libraryModules ??= findLibraryModules(rootPath);
|
|
1923
|
+
let modules = new Set();
|
|
1924
|
+
const rebuilds = new Map();
|
|
1925
|
+
return {
|
|
1926
|
+
fileFor: (environmentName, entryName)=>{
|
|
1927
|
+
const file = node_path.join(cacheDir, environmentName, `${entryName}.js`);
|
|
1928
|
+
let environmentFiles = files.get(environmentName);
|
|
1929
|
+
if (!environmentFiles) {
|
|
1930
|
+
environmentFiles = new Set();
|
|
1931
|
+
files.set(environmentName, environmentFiles);
|
|
1932
|
+
}
|
|
1933
|
+
environmentFiles.add(file);
|
|
1934
|
+
if (!node_fs.existsSync(file)) writeIfChanged(file, renderBackgroundEntry({
|
|
1935
|
+
modules: []
|
|
1936
|
+
}));
|
|
1937
|
+
return file;
|
|
1938
|
+
},
|
|
1939
|
+
isBackgroundModule: (file)=>modules.has(file),
|
|
1940
|
+
isEntryFile: (file)=>[
|
|
1941
|
+
...files.values()
|
|
1942
|
+
].some((environmentFiles)=>environmentFiles.has(file)),
|
|
1943
|
+
update: (environmentName, { appModules, watch, rebuilt })=>{
|
|
1944
|
+
modules = new Set([
|
|
1945
|
+
...getLibraryModules(),
|
|
1946
|
+
...[
|
|
1947
|
+
...appModules
|
|
1948
|
+
].sort()
|
|
1949
|
+
]);
|
|
1950
|
+
const rebuild = (rebuilds.get(environmentName) ?? 0) + (watch && rebuilt ? 1 : 0);
|
|
1951
|
+
rebuilds.set(environmentName, rebuild);
|
|
1952
|
+
const contents = renderBackgroundEntry({
|
|
1953
|
+
modules: [
|
|
1954
|
+
...modules
|
|
1955
|
+
],
|
|
1956
|
+
rebuild: watch ? rebuild : void 0
|
|
1957
|
+
});
|
|
1958
|
+
return [
|
|
1959
|
+
...files.get(environmentName) ?? []
|
|
1960
|
+
].filter((file)=>writeIfChanged(file, contents));
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
};
|
|
1964
|
+
const renderBackgroundEntry = ({ modules, rebuild })=>{
|
|
1965
|
+
const lines = [
|
|
1966
|
+
"// The background thread's entry, generated by",
|
|
1967
|
+
'// @blotch/rsbuild-plugin-angular-lynx (see background-entry.ts there).',
|
|
1968
|
+
'// Angular runs on the main thread only, so the background thread loads the',
|
|
1969
|
+
'// runtime and the modules that call backgroundFn(), createMainThreadRef()',
|
|
1970
|
+
'// or new MainThreadRef() at module level.',
|
|
1971
|
+
`import { ɵstartBackgroundThread } from '${background_entry_RUNTIME_MODULE}';`,
|
|
1972
|
+
...modules.map((file)=>`import ${JSON.stringify(file)};`),
|
|
1973
|
+
'ɵstartBackgroundThread();'
|
|
1974
|
+
];
|
|
1975
|
+
if (void 0 !== rebuild) lines.push(`// Rebuild ${rebuild}`);
|
|
1976
|
+
return `${lines.join('\n')}\n`;
|
|
1977
|
+
};
|
|
1978
|
+
const writeIfChanged = (file, contents)=>{
|
|
1979
|
+
if (node_fs.existsSync(file) && node_fs.readFileSync(file, 'utf8') === contents) return false;
|
|
1980
|
+
node_fs.mkdirSync(node_path.dirname(file), {
|
|
1981
|
+
recursive: true
|
|
1501
1982
|
});
|
|
1983
|
+
node_fs.writeFileSync(file, contents);
|
|
1984
|
+
return true;
|
|
1985
|
+
};
|
|
1986
|
+
const JS_FILE = /\.[cm]?js$/;
|
|
1987
|
+
const findLibraryBackgroundModules = (rootPath)=>{
|
|
1988
|
+
const found = new Set();
|
|
1989
|
+
const visited = new Set();
|
|
1990
|
+
const visit = (packageDir, isApp)=>{
|
|
1991
|
+
const manifest = readManifest(packageDir);
|
|
1992
|
+
if (!manifest) return;
|
|
1993
|
+
const names = new Set([
|
|
1994
|
+
...Object.keys(manifest.dependencies ?? {}),
|
|
1995
|
+
...Object.keys(manifest.optionalDependencies ?? {}),
|
|
1996
|
+
...Object.keys(manifest.peerDependencies ?? {}),
|
|
1997
|
+
...isApp ? Object.keys(manifest.devDependencies ?? {}) : []
|
|
1998
|
+
]);
|
|
1999
|
+
names.delete(background_entry_RUNTIME_MODULE);
|
|
2000
|
+
for (const name of names){
|
|
2001
|
+
const dependencyDir = resolvePackageDir(name, packageDir);
|
|
2002
|
+
if (!dependencyDir || visited.has(dependencyDir)) continue;
|
|
2003
|
+
visited.add(dependencyDir);
|
|
2004
|
+
const dependency = readManifest(dependencyDir);
|
|
2005
|
+
if (dependency && dependsOnRuntime(dependency)) {
|
|
2006
|
+
for (const file of listJsFiles(dependencyDir))if (definesCrossThreadValues(node_fs.readFileSync(file, 'utf8'), file)) found.add(file);
|
|
2007
|
+
}
|
|
2008
|
+
visit(dependencyDir, false);
|
|
2009
|
+
}
|
|
2010
|
+
};
|
|
2011
|
+
visit(rootPath, true);
|
|
2012
|
+
return [
|
|
2013
|
+
...found
|
|
2014
|
+
].sort();
|
|
2015
|
+
};
|
|
2016
|
+
const readManifest = (packageDir)=>{
|
|
2017
|
+
try {
|
|
2018
|
+
return JSON.parse(node_fs.readFileSync(node_path.join(packageDir, 'package.json'), 'utf8'));
|
|
2019
|
+
} catch {
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
const dependsOnRuntime = (manifest)=>[
|
|
2024
|
+
manifest.dependencies,
|
|
2025
|
+
manifest.optionalDependencies,
|
|
2026
|
+
manifest.peerDependencies
|
|
2027
|
+
].some((dependencies)=>dependencies?.[background_entry_RUNTIME_MODULE] !== void 0);
|
|
2028
|
+
const resolvePackageDir = (name, fromDir)=>{
|
|
2029
|
+
for(let dir = fromDir;; dir = node_path.dirname(dir)){
|
|
2030
|
+
const candidate = node_path.join(dir, 'node_modules', name);
|
|
2031
|
+
if (node_fs.existsSync(node_path.join(candidate, 'package.json'))) return node_fs.realpathSync(candidate);
|
|
2032
|
+
if (node_path.dirname(dir) === dir) return;
|
|
2033
|
+
}
|
|
1502
2034
|
};
|
|
2035
|
+
const listJsFiles = (packageDir)=>node_fs.readdirSync(packageDir, {
|
|
2036
|
+
recursive: true,
|
|
2037
|
+
encoding: 'utf8'
|
|
2038
|
+
}).filter((file)=>JS_FILE.test(file) && !file.split(node_path.sep).includes('node_modules')).map((file)=>node_path.join(packageDir, file));
|
|
1503
2039
|
const applyCSS = (api, options)=>{
|
|
1504
2040
|
const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
|
|
1505
2041
|
api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
|
|
@@ -1525,23 +2061,23 @@ const applyCSS = (api, options)=>{
|
|
|
1525
2061
|
const mainRule = rule.oneOf(mainRuleName);
|
|
1526
2062
|
const parentRuleEntries = rule.entries();
|
|
1527
2063
|
removeLightningCSS(mainRule);
|
|
1528
|
-
mainRule.issuerLayer(LAYERS.
|
|
2064
|
+
mainRule.issuerLayer(LAYERS.MAIN_THREAD).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
|
|
1529
2065
|
const uses = mainRule.uses.entries() ?? {};
|
|
1530
2066
|
const ruleEntries = mainRule.entries();
|
|
1531
2067
|
const cssLoader = uses[CHAIN_ID.USE.CSS];
|
|
1532
2068
|
if (!cssLoader) continue;
|
|
1533
2069
|
const cssLoaderRule = cssLoader.entries();
|
|
1534
|
-
const
|
|
1535
|
-
if (void 0 !== parentRuleEntries.dependency)
|
|
2070
|
+
const backgroundLayerRule = chain.module.rule(`${ruleName}:${LAYERS.BACKGROUND}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.BACKGROUND);
|
|
2071
|
+
if (void 0 !== parentRuleEntries.dependency) backgroundLayerRule.merge({
|
|
1536
2072
|
dependency: parentRuleEntries.dependency
|
|
1537
2073
|
});
|
|
1538
|
-
|
|
2074
|
+
backgroundLayerRule.use(CHAIN_ID.USE.IGNORE_CSS).loader(node_path.resolve(__dirname, './loaders/ignore-css-loader')).end().uses.merge(uses).delete(CHAIN_ID.USE.MINI_CSS_EXTRACT).delete(CHAIN_ID.USE.LIGHTNINGCSS).delete(CHAIN_ID.USE.CSS).end().use(CHAIN_ID.USE.CSS).after(CHAIN_ID.USE.IGNORE_CSS).merge(cssLoaderRule).options(normalizeCssLoaderOptions(cssLoaderRule.options, true)).end();
|
|
1539
2075
|
}
|
|
1540
2076
|
for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
|
|
1541
2077
|
const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
|
|
1542
2078
|
removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
|
|
1543
2079
|
}
|
|
1544
|
-
if (
|
|
2080
|
+
if (chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
|
|
1545
2081
|
const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
|
|
1546
2082
|
fontRule.oneOfs.clear();
|
|
1547
2083
|
fontRule.type('asset/inline');
|
|
@@ -1597,6 +2133,21 @@ const applyGenerator = (api)=>{
|
|
|
1597
2133
|
}
|
|
1598
2134
|
});
|
|
1599
2135
|
};
|
|
2136
|
+
const applyResolve = (api)=>{
|
|
2137
|
+
api.modifyRspackConfig((config)=>{
|
|
2138
|
+
const resolve = config.resolve ??= {};
|
|
2139
|
+
const conditions = (resolve.conditionNames ?? []).filter((condition)=>'require' !== condition);
|
|
2140
|
+
if (!conditions.includes('module')) conditions.splice(conditions.indexOf('import') + 1, 0, 'module');
|
|
2141
|
+
resolve.byDependency = {
|
|
2142
|
+
...resolve.byDependency,
|
|
2143
|
+
esm: {
|
|
2144
|
+
...resolve.byDependency?.['esm'],
|
|
2145
|
+
conditionNames: conditions
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
});
|
|
2149
|
+
};
|
|
2150
|
+
const DEV_LOGS_PATH = '/__dev_logs';
|
|
1600
2151
|
const RESET = '\x1b[0m';
|
|
1601
2152
|
const GRAY = '\x1b[90m';
|
|
1602
2153
|
const RED = '\x1b[31m';
|
|
@@ -1631,7 +2182,9 @@ const formatArg = (arg)=>{
|
|
|
1631
2182
|
if ('string' == typeof arg) return arg;
|
|
1632
2183
|
if ('object' == typeof arg && 'Error' === arg.__type) {
|
|
1633
2184
|
const err = arg;
|
|
1634
|
-
|
|
2185
|
+
const header = err.message ? `${err.name}: ${err.message}` : err.name;
|
|
2186
|
+
if (!err.stack) return header;
|
|
2187
|
+
return err.stack.startsWith(header) ? err.stack : `${header}\n${err.stack}`;
|
|
1635
2188
|
}
|
|
1636
2189
|
try {
|
|
1637
2190
|
return JSON.stringify(arg, null, 2);
|
|
@@ -1639,28 +2192,19 @@ const formatArg = (arg)=>{
|
|
|
1639
2192
|
return String(arg);
|
|
1640
2193
|
}
|
|
1641
2194
|
};
|
|
1642
|
-
|
|
1643
|
-
let logFilePath = '';
|
|
1644
|
-
const getLogFilePath = ()=>logFilePath;
|
|
1645
|
-
const ensureLogFile = ()=>{
|
|
1646
|
-
if (!logFile) {
|
|
1647
|
-
logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
|
|
1648
|
-
logFile = node_fs.createWriteStream(logFilePath, {
|
|
1649
|
-
flags: 'w'
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
return logFile;
|
|
1653
|
-
};
|
|
1654
|
-
const writeLog = (entry)=>{
|
|
1655
|
-
const stream = ensureLogFile();
|
|
2195
|
+
const writeLog = (stream, entry)=>{
|
|
1656
2196
|
const color = LEVEL_COLORS[entry.level] || WHITE;
|
|
1657
2197
|
const label = LEVEL_LABELS[entry.level] || entry.level.toUpperCase();
|
|
1658
2198
|
const time = formatTimestamp(entry.timestamp);
|
|
1659
2199
|
const args = entry.args.map(formatArg).join(' ');
|
|
1660
2200
|
stream.write(`${GRAY}${time}${RESET} ${color}${label}${RESET} ${args}\n`);
|
|
1661
2201
|
};
|
|
1662
|
-
const createDevLoggerMiddleware = ()=>
|
|
1663
|
-
|
|
2202
|
+
const createDevLoggerMiddleware = (logFilePath)=>{
|
|
2203
|
+
const logFile = node_fs.createWriteStream(logFilePath, {
|
|
2204
|
+
flags: 'w'
|
|
2205
|
+
});
|
|
2206
|
+
return (req, res, next)=>{
|
|
2207
|
+
if ('OPTIONS' === req.method && req.url === DEV_LOGS_PATH) {
|
|
1664
2208
|
res.writeHead(204, {
|
|
1665
2209
|
'Access-Control-Allow-Origin': '*',
|
|
1666
2210
|
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
@@ -1669,7 +2213,7 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
|
|
|
1669
2213
|
res.end();
|
|
1670
2214
|
return;
|
|
1671
2215
|
}
|
|
1672
|
-
if ('POST' !== req.method ||
|
|
2216
|
+
if ('POST' !== req.method || req.url !== DEV_LOGS_PATH) return void next();
|
|
1673
2217
|
let body = '';
|
|
1674
2218
|
req.on('data', (chunk)=>{
|
|
1675
2219
|
body += chunk.toString();
|
|
@@ -1681,22 +2225,21 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
|
|
|
1681
2225
|
res.end();
|
|
1682
2226
|
try {
|
|
1683
2227
|
const { logs } = JSON.parse(body);
|
|
1684
|
-
for (const entry of logs)writeLog(entry);
|
|
2228
|
+
for (const entry of logs)writeLog(logFile, entry);
|
|
1685
2229
|
} catch {}
|
|
1686
2230
|
});
|
|
1687
2231
|
};
|
|
1688
|
-
|
|
2232
|
+
};
|
|
2233
|
+
const FALLBACK_ORIGIN = 'http://localhost:3000';
|
|
1689
2234
|
const applyDevLogger = (api)=>{
|
|
1690
2235
|
api.modifyRspackConfig((rspackConfig)=>{
|
|
1691
2236
|
if ('development' !== rspackConfig.mode) return rspackConfig;
|
|
1692
2237
|
const publicPath = rspackConfig.output?.publicPath;
|
|
1693
|
-
let
|
|
2238
|
+
let origin = FALLBACK_ORIGIN;
|
|
1694
2239
|
if ('string' == typeof publicPath && publicPath.startsWith('http')) try {
|
|
1695
|
-
|
|
1696
|
-
const logPort = Number(parsed.port || 80) + LOG_PORT_OFFSET;
|
|
1697
|
-
logUrl = `${parsed.protocol}//${parsed.hostname}:${logPort}/__dev_logs`;
|
|
2240
|
+
origin = new URL(publicPath).origin;
|
|
1698
2241
|
} catch {}
|
|
1699
|
-
|
|
2242
|
+
const logUrl = `${origin}${DEV_LOGS_PATH}`;
|
|
1700
2243
|
rspackConfig.plugins = rspackConfig.plugins || [];
|
|
1701
2244
|
rspackConfig.plugins.push({
|
|
1702
2245
|
name: 'lynx:angular:dev-logger-define',
|
|
@@ -1709,22 +2252,12 @@ const applyDevLogger = (api)=>{
|
|
|
1709
2252
|
});
|
|
1710
2253
|
return rspackConfig;
|
|
1711
2254
|
});
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
res.end();
|
|
1719
|
-
});
|
|
1720
|
-
});
|
|
1721
|
-
server.listen(logPort, '0.0.0.0', ()=>{
|
|
1722
|
-
console.log(`[dev-logger] Log server on :${logPort} → tail -f ${getLogFilePath()}`);
|
|
1723
|
-
});
|
|
1724
|
-
server.on('error', (err)=>{
|
|
1725
|
-
if ('EADDRINUSE' === err.code) server.listen(logPort + 1, '0.0.0.0');
|
|
1726
|
-
else console.warn('[dev-logger] Failed to start log server:', err.message);
|
|
1727
|
-
});
|
|
2255
|
+
const logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
|
|
2256
|
+
api.onBeforeStartDevServer(({ server })=>{
|
|
2257
|
+
server.middlewares.use(createDevLoggerMiddleware(logFilePath));
|
|
2258
|
+
});
|
|
2259
|
+
api.onAfterStartDevServer(()=>{
|
|
2260
|
+
console.log(`[dev-logger] LynxLogger output: tail -f ${logFilePath}`);
|
|
1728
2261
|
});
|
|
1729
2262
|
};
|
|
1730
2263
|
const applySplitChunksRule = (api, options)=>{
|
|
@@ -1760,16 +2293,28 @@ const TAILWIND_CONFIG_FILES = [
|
|
|
1760
2293
|
'tailwind.config.mjs',
|
|
1761
2294
|
'tailwind.config.cjs'
|
|
1762
2295
|
];
|
|
2296
|
+
const isTailwind = (plugin)=>'function' == typeof plugin ? 'tailwindcss' === plugin.name : plugin?.postcssPlugin === 'tailwindcss';
|
|
1763
2297
|
const applyTailwind = (api)=>{
|
|
1764
2298
|
const configFile = findUp(TAILWIND_CONFIG_FILES, process.cwd());
|
|
1765
2299
|
if (!configFile) return;
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
2300
|
+
api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
|
|
2301
|
+
tools: {
|
|
2302
|
+
postcss: ({ postcssOptions }, { addPlugins })=>{
|
|
2303
|
+
if ('object' == typeof postcssOptions && postcssOptions.plugins?.some(isTailwind)) return;
|
|
2304
|
+
const tailwindcss = createRequire(configFile)('tailwindcss');
|
|
2305
|
+
addPlugins(tailwindcss({
|
|
2306
|
+
config: configFile
|
|
2307
|
+
}));
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}));
|
|
1772
2311
|
};
|
|
2312
|
+
const DEPRECATED_OPTIONS = [
|
|
2313
|
+
'enableICU',
|
|
2314
|
+
'enableParallelElement',
|
|
2315
|
+
'pipelineSchedulerConfig'
|
|
2316
|
+
];
|
|
2317
|
+
const deprecatedOptionsIn = (options)=>DEPRECATED_OPTIONS.filter((name)=>options?.[name] !== void 0);
|
|
1773
2318
|
const normalizeOptions = (options)=>{
|
|
1774
2319
|
const defaultOptions = {
|
|
1775
2320
|
customCSSInheritanceList: void 0,
|
|
@@ -1779,20 +2324,19 @@ const normalizeOptions = (options)=>{
|
|
|
1779
2324
|
enableCSSInheritance: false,
|
|
1780
2325
|
enableCSSInvalidation: false,
|
|
1781
2326
|
enableCSSSelector: true,
|
|
1782
|
-
enableICU: false,
|
|
1783
2327
|
enableNewGesture: true,
|
|
1784
|
-
enableParallelElement: true,
|
|
1785
2328
|
defaultDisplayLinear: true,
|
|
1786
2329
|
enableRemoveCSSScope: false,
|
|
1787
2330
|
enableSSR: false,
|
|
1788
2331
|
pages: void 0,
|
|
1789
|
-
pipelineSchedulerConfig: 0x00010000,
|
|
1790
2332
|
targetSdkVersion: '3.2',
|
|
1791
2333
|
defaultOverflowVisible: true,
|
|
1792
2334
|
removeDescendantSelectorScope: false,
|
|
1793
2335
|
experimental_isLazyBundle: false
|
|
1794
2336
|
};
|
|
1795
|
-
|
|
2337
|
+
const normalized = Object.assign(defaultOptions, options);
|
|
2338
|
+
for (const name of DEPRECATED_OPTIONS)delete normalized[name];
|
|
2339
|
+
return normalized;
|
|
1796
2340
|
};
|
|
1797
2341
|
const pluginAngularLynx = (options)=>({
|
|
1798
2342
|
name: 'lynx:angular',
|
|
@@ -1801,12 +2345,18 @@ const pluginAngularLynx = (options)=>({
|
|
|
1801
2345
|
ANGULAR_CLI_CONTEXT_PLUGIN_NAME
|
|
1802
2346
|
],
|
|
1803
2347
|
setup: async (api)=>{
|
|
2348
|
+
for (const name of deprecatedOptionsIn(options))api.logger.warn(`pluginAngularLynx(): the \`${name}\` option has no effect and will be removed in a future release. Remove it from your config.`);
|
|
1804
2349
|
const normalizedOptions = normalizeOptions(options);
|
|
2350
|
+
const backgroundEntries = createBackgroundEntries({
|
|
2351
|
+
rootPath: api.context.rootPath
|
|
2352
|
+
});
|
|
1805
2353
|
applyCSS(api, normalizedOptions);
|
|
1806
2354
|
applyTailwind(api);
|
|
1807
|
-
|
|
2355
|
+
const appEntries = new Set();
|
|
2356
|
+
applyEntry(api, normalizedOptions, backgroundEntries, appEntries);
|
|
1808
2357
|
applyLayers(api);
|
|
1809
|
-
|
|
2358
|
+
applyResolve(api);
|
|
2359
|
+
const angularRules = applyAngularRules(api, normalizedOptions, backgroundEntries, appEntries);
|
|
1810
2360
|
applyGenerator(api);
|
|
1811
2361
|
applySplitChunksRule(api, normalizedOptions);
|
|
1812
2362
|
applyDevLogger(api);
|