@blotch/rsbuild-plugin-angular-lynx 22.2.1 → 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.
Files changed (63) hide show
  1. package/README.md +20 -3
  2. package/dist/544.js +21 -0
  3. package/dist/angular-cli-context.d.ts +34 -0
  4. package/dist/angular-diagnostics.d.ts +58 -0
  5. package/dist/angular-diagnostics.test.d.ts +1 -0
  6. package/dist/angular-rebuild-coordinator.d.ts +1 -1
  7. package/dist/angular.d.ts +5 -3
  8. package/dist/background-entry.d.ts +96 -0
  9. package/dist/background-entry.test.d.ts +1 -0
  10. package/dist/budgets-plugin.d.ts +64 -0
  11. package/dist/budgets-plugin.test.d.ts +1 -0
  12. package/dist/client/background-hot-updates.d.ts +50 -0
  13. package/dist/client/background-hot-updates.js +77 -0
  14. package/dist/client/background-hot-updates.test.d.ts +1 -0
  15. package/dist/client/css-hot-update.cjs +9 -0
  16. package/dist/client/css-hot-update.d.ts +44 -0
  17. package/dist/client/css-hot-update.js +42 -0
  18. package/dist/client/css-hot-update.test.d.ts +1 -0
  19. package/dist/client/hmr-background.js +16 -0
  20. package/dist/client/hmr-main-thread.js +9 -0
  21. package/dist/client/hmr-protocol.d.ts +74 -0
  22. package/dist/client/main-thread-hot-updates.d.ts +40 -0
  23. package/dist/client/main-thread-hot-updates.js +78 -0
  24. package/dist/client/main-thread-hot-updates.test.d.ts +1 -0
  25. package/dist/client/testing/test-context.d.ts +16 -0
  26. package/dist/component-styles-cache.d.ts +13 -31
  27. package/dist/component-styles.d.ts +73 -0
  28. package/dist/component-styles.test.d.ts +1 -0
  29. package/dist/cross-thread-transform.d.ts +38 -0
  30. package/dist/cross-thread-transform.test.d.ts +1 -0
  31. package/dist/css.d.ts +2 -2
  32. package/dist/entry.d.ts +28 -2
  33. package/dist/hot-updates.d.ts +67 -0
  34. package/dist/hot-updates.test.d.ts +1 -0
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.js +1494 -638
  37. package/dist/layers.d.ts +6 -4
  38. package/dist/logger/dev-logger-middleware.d.ts +13 -7
  39. package/dist/logger/dev-logger.d.ts +13 -0
  40. package/dist/polyfills.js +56 -0
  41. package/dist/polyfills.test.d.ts +1 -0
  42. package/dist/pure-classes.d.ts +27 -0
  43. package/dist/pure-classes.test.d.ts +1 -0
  44. package/dist/resolve.d.ts +19 -0
  45. package/dist/resolve.test.d.ts +1 -0
  46. package/dist/split-chunks.d.ts +2 -2
  47. package/dist/styles/blur-view.css +16 -0
  48. package/dist/transform-module.d.ts +43 -17
  49. package/dist/utils/angular/budgets.d.ts +72 -0
  50. package/dist/utils/angular/budgets.test.d.ts +1 -0
  51. package/dist/utils/angular/build-internals.d.ts +14 -0
  52. package/dist/utils/angular/component-scope-id.d.ts +5 -3
  53. package/dist/utils/angular/component-style-bundler.d.ts +1 -1
  54. package/dist/utils/angular/options.d.ts +80 -2
  55. package/dist/utils/angular/read-workspace.d.ts +5 -1
  56. package/dist/utils/angular/resolve-primary-build.d.ts +26 -0
  57. package/dist/utils/angular/resolve-primary-build.test.d.ts +1 -0
  58. package/dist/utils/options.d.ts +40 -1
  59. package/package.json +13 -8
  60. package/dist/loaders/thread-globals-loader.d.ts +0 -8
  61. package/dist/loaders/thread-globals-loader.js +0 -6
  62. package/patch/patch-angular-build.cjs +0 -27
  63. /package/dist/{loaders/thread-globals-loader.test.d.ts → angular-cli-context.test.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,23 +1,29 @@
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";
4
- import { DiagnosticModes, createAngularCompilation } from "@angular/build/src/tools/angular/compilation";
5
- import { JavaScriptTransformer } from "@angular/build/src/tools/javascript-transformer";
6
- import { createHash } from "node:crypto";
7
- import { fileURLToPath } from "node:url";
8
- import { availableParallelism } from "node:os";
9
- import node_assert from "node:assert";
10
- import { access, constants } from "node:fs/promises";
11
- import { workspaces } from "@angular-devkit/core";
12
- import { NodeJsSyncHost } from "@angular-devkit/core/node";
3
+ import node_path, { join, resolve as external_node_path_resolve } from "node:path";
13
4
  import { CSSPlugins, LynxEncodePlugin, LynxTemplatePlugin, WebEncodePlugin } from "@lynx-js/template-webpack-plugin";
5
+ import { createRequire } from "node:module";
6
+ import { fileURLToPath } from "node:url";
14
7
  import { RuntimeWrapperWebpackPlugin } from "@lynx-js/runtime-wrapper-webpack-plugin";
15
8
  import { RuntimeGlobals } from "@lynx-js/webpack-runtime-globals";
16
9
  import tiny_invariant from "tiny-invariant";
17
- import node_http from "node:http";
18
- import { pluginTailwindCSS } from "rsbuild-plugin-tailwindcss";
10
+ import { createHash } from "node:crypto";
11
+ import { availableParallelism } from "node:os";
12
+ import { workspaces } from "@angular-devkit/core";
13
+ import { NodeJsSyncHost } from "@angular-devkit/core/node";
14
+ import { access, constants } from "node:fs/promises";
19
15
  import * as __rspack_external_typescript from "typescript";
20
16
  import * as __rspack_external_node_path_c5b9b54f from "node:path";
17
+ const ANGULAR_CLI_CONTEXT = Symbol.for('@blotch/rsbuild-plugin-angular-lynx:angular-cli-context');
18
+ const ANGULAR_CLI_CONTEXT_PLUGIN_NAME = 'lynx:angular-cli-context';
19
+ const pluginAngularCliContext = (context)=>({
20
+ name: ANGULAR_CLI_CONTEXT_PLUGIN_NAME,
21
+ enforce: 'pre',
22
+ setup: (api)=>{
23
+ api.expose(ANGULAR_CLI_CONTEXT, context);
24
+ }
25
+ });
26
+ const getAngularCliContext = (api)=>api.useExposed(ANGULAR_CLI_CONTEXT);
21
27
  const TYPESCRIPT_FILE = /\.[cm]?tsx?$/;
22
28
  const readFileVersion = (file)=>node_fs.statSync(file, {
23
29
  throwIfNoEntry: false
@@ -64,17 +70,572 @@ const createAngularRebuildCoordinator = ({ compile, getFileVersion = readFileVer
64
70
  pendingFiles.add(file);
65
71
  }
66
72
  }
67
- const run = queue.then(runCompile);
68
- queue = run.catch(()=>{});
69
- await run;
70
- const lastSeen = environmentGenerations.get(environmentName);
71
- environmentGenerations.set(environmentName, generation);
72
- const outdated = new Set();
73
- if (void 0 === lastSeen) return outdated;
74
- for (const [file, outputGeneration] of outputGenerations)if (outputGeneration > lastSeen) outdated.add(file);
75
- return outdated;
76
- }
77
- };
73
+ const run = queue.then(runCompile);
74
+ queue = run.catch(()=>{});
75
+ await run;
76
+ const lastSeen = environmentGenerations.get(environmentName);
77
+ environmentGenerations.set(environmentName, generation);
78
+ const outdated = new Set();
79
+ if (void 0 === lastSeen) return outdated;
80
+ for (const [file, outputGeneration] of outputGenerations)if (outputGeneration > lastSeen) outdated.add(file);
81
+ return outdated;
82
+ }
83
+ };
84
+ };
85
+ const LAYERS = {
86
+ BACKGROUND: 'background',
87
+ MAIN_THREAD: 'main'
88
+ };
89
+ const applyLayers = (api)=>{
90
+ api.modifyBundlerChain((chain)=>{
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({
103
+ jsc: {
104
+ target,
105
+ parser: {
106
+ syntax: "typescript"
107
+ },
108
+ transform: {
109
+ optimizer: {
110
+ simplify: false,
111
+ globals: {
112
+ vars: {
113
+ __MAIN_THREAD__: String(isMainThread)
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }).end();
120
+ });
121
+ };
122
+ const createLynxProcessEvalResultRuntimeModule = (webpack)=>class extends webpack.RuntimeModule {
123
+ constructor(){
124
+ super('Lynx process eval result', webpack.RuntimeModule.STAGE_ATTACH);
125
+ }
126
+ generate() {
127
+ const chunk = this.chunk;
128
+ const compilation = this.compilation;
129
+ if (!chunk || !compilation) return '';
130
+ const chunkGroup = chunk._groupsIterable?.[0];
131
+ const modules = compilation.chunkGraph?.getChunkModules(chunk) ?? [];
132
+ modules.sort((a, b)=>{
133
+ const aIdx = chunkGroup?.getModulePreOrderIndex(a) ?? 0;
134
+ const bIdx = chunkGroup?.getModulePreOrderIndex(b) ?? 0;
135
+ return aIdx - bIdx;
136
+ });
137
+ const sortedIds = modules.map((m)=>compilation.chunkGraph.getModuleId(m)).filter((id)=>null != id);
138
+ const moduleOrder = JSON.stringify(sortedIds);
139
+ return `
140
+ ${RuntimeGlobals.lynxProcessEvalResult} = function (result, schema) {
141
+ var chunk = result && result(schema);
142
+ if (chunk && chunk.ids && chunk.modules) {
143
+ ${webpack.RuntimeGlobals.externalInstallChunk}(chunk);
144
+ var moduleOrder = ${moduleOrder};
145
+ for (var i = 0; i < moduleOrder.length; i++) {
146
+ if (chunk.modules[moduleOrder[i]]) {
147
+ ${webpack.RuntimeGlobals.require}(moduleOrder[i]);
148
+ }
149
+ }
150
+ return chunk;
151
+ }
152
+ return chunk
153
+ }
154
+ `;
155
+ }
156
+ };
157
+ class AngularWebpackPlugin {
158
+ options;
159
+ constructor(options){
160
+ this.options = options;
161
+ }
162
+ static defaultOptions = Object.freeze({
163
+ disableCreateSelectorQueryIncompatibleWarning: false,
164
+ firstScreenSyncTiming: 'immediately',
165
+ enableSSR: false,
166
+ isWeb: false,
167
+ mainThreadChunks: [],
168
+ extractStr: false,
169
+ experimental_isLazyBundle: false
170
+ });
171
+ apply(compiler) {
172
+ const options = Object.assign({}, AngularWebpackPlugin.defaultOptions, this.options);
173
+ if (options.enableSSR && 'jsReady' !== options.firstScreenSyncTiming) options.firstScreenSyncTiming = 'jsReady';
174
+ const { DefinePlugin, EnvironmentPlugin } = compiler.webpack;
175
+ new EnvironmentPlugin({
176
+ DEBUG: null
177
+ }).apply(compiler);
178
+ new DefinePlugin({
179
+ __DEV__: JSON.stringify('development' === compiler.options.mode),
180
+ __PROFILE__: JSON.stringify('development' === compiler.options.mode),
181
+ __EXTRACT_STR__: JSON.stringify(Boolean(options.extractStr)),
182
+ __FIRST_SCREEN_SYNC_TIMING__: JSON.stringify(options.firstScreenSyncTiming),
183
+ __ENABLE_SSR__: JSON.stringify(options.enableSSR),
184
+ __WEB__: JSON.stringify(Boolean(options.isWeb)),
185
+ __DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__: JSON.stringify(options.disableCreateSelectorQueryIncompatibleWarning)
186
+ }).apply(compiler);
187
+ compiler.hooks.thisCompilation.tap(this.constructor.name, (compilation)=>{
188
+ const onceForChunkSet = new WeakSet();
189
+ compilation.hooks.runtimeRequirementInTree.for(compiler.webpack.RuntimeGlobals.ensureChunkHandlers).tap('VanillaWebpackPlugin', (chunk, runtimeRequirements)=>{
190
+ runtimeRequirements.add(RuntimeGlobals.lynxProcessEvalResult);
191
+ });
192
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.lynxProcessEvalResult).tap('VanillaWebpackPlugin', (chunk)=>{
193
+ if (onceForChunkSet.has(chunk)) return;
194
+ onceForChunkSet.add(chunk);
195
+ const LynxProcessEvalResultRuntimeModule = createLynxProcessEvalResultRuntimeModule(compiler.webpack);
196
+ compilation.addRuntimeModule(chunk, new LynxProcessEvalResultRuntimeModule());
197
+ });
198
+ compilation.hooks.processAssets.tap({
199
+ name: this.constructor.name,
200
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
201
+ }, ()=>{
202
+ for (const name of options.mainThreadChunks ?? [])this.#updateMainThreadInfo(compilation, name);
203
+ for (const cg of compilation.chunkGroups.filter((cg)=>!cg.isInitial()).filter((cg)=>cg.origins.every((origin)=>origin.module?.layer === LAYERS.MAIN_THREAD))){
204
+ const files = cg.getFiles();
205
+ for (const name of files.filter((name)=>name.endsWith('.js')))this.#updateMainThreadInfo(compilation, name);
206
+ }
207
+ });
208
+ {
209
+ const { ConcatSource } = compiler.webpack.sources;
210
+ const mainThreadChunkSet = new Set(options.mainThreadChunks ?? []);
211
+ compilation.hooks.processAssets.tap({
212
+ name: `${this.constructor.name}:threadGlobals`,
213
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
214
+ }, ()=>{
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;
218
+ const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
219
+ compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
220
+ }
221
+ if (!options.experimental_isLazyBundle) for (const name of options.mainThreadChunks ?? []){
222
+ const asset = compilation.getAsset(name);
223
+ if (!asset) continue;
224
+ const source = asset.source.source().toString();
225
+ const useStrictPrefix = /^(['"])use strict\1;?/.test(source) ? "'use strict';" : '';
226
+ compilation.updateAsset(name, (old)=>new ConcatSource(`${useStrictPrefix}var globDynamicComponentEntry=globDynamicComponentEntry||'__Card__';`, old));
227
+ }
228
+ });
229
+ }
230
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
231
+ const { ConcatSource } = compiler.webpack.sources;
232
+ hooks.beforeEncode.tap(this.constructor.name, (args)=>{
233
+ const { encodeData } = args;
234
+ encodeData.sourceContent.config = {
235
+ ...encodeData.sourceContent.config,
236
+ enableCSSInlineVariables: true
237
+ };
238
+ if (!encodeData.lepusCode.root) return args;
239
+ if ('card' === encodeData.sourceContent.appType) return args;
240
+ compilation.updateAsset(encodeData.lepusCode.root.name, (old)=>new ConcatSource(`\
241
+ (function (globDynamicComponentEntry) {
242
+ const module = { exports: {} }
243
+ const exports = module.exports
244
+ `, old, `
245
+ ;return module.exports
246
+ })`));
247
+ return args;
248
+ });
249
+ });
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
+ }
261
+ #updateMainThreadInfo(compilation, name) {
262
+ const asset = compilation.getAsset(name);
263
+ tiny_invariant(asset, `Should have main thread asset ${name}`);
264
+ compilation.updateAsset(asset.name, asset.source, {
265
+ ...asset.info,
266
+ 'lynx:main-thread': true
267
+ });
268
+ }
269
+ }
270
+ const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
271
+ const PLUGIN_NAME_TEMPLATE = 'lynx:template';
272
+ const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
273
+ const PLUGIN_NAME_ANGULAR = 'lynx:angular';
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 })=>{
285
+ const lynxConfig = api.useExposed(S_LYNX_CONFIG);
286
+ if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
287
+ const isLynx = 'lynx' === environment.name;
288
+ const isWeb = 'web' === environment.name;
289
+ const { hmr, liveReload } = environment.config.dev ?? {};
290
+ const enabledHMR = isDev && !isWeb && false !== hmr;
291
+ const enabledLiveReload = isDev && !isWeb && false !== liveReload;
292
+ const entries = chain.entryPoints.entries() ?? {};
293
+ chain.entryPoints.clear();
294
+ const preEntry = new Set(environment.config.source.preEntry);
295
+ const mainThreadChunks = [];
296
+ for (const [entryName, entryPoint] of Object.entries(entries)){
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));
307
+ const templateFilename = lynxConfig.resolveBundleFilename({
308
+ entryName,
309
+ platform: environment.name
310
+ });
311
+ const lazyBundleFilename = lynxConfig.resolveLazyBundleFilename({
312
+ platform: environment.name
313
+ });
314
+ const mainThreadEntry = `${entryName}__main-thread`;
315
+ const mainThreadName = node_path.posix.join(`${entryName}/main-thread.js`);
316
+ const backgroundEntry = entryName;
317
+ const backgroundThreadName = node_path.posix.join(`${entryName}/background-thread.js`);
318
+ mainThreadChunks.push(mainThreadName);
319
+ chain.entry(mainThreadEntry).add({
320
+ layer: LAYERS.MAIN_THREAD,
321
+ import: mainThreadImports,
322
+ filename: mainThreadName
323
+ }).when(enabledHMR, (entry)=>{
324
+ entry.prepend({
325
+ layer: LAYERS.MAIN_THREAD,
326
+ import: HMR_MAIN_THREAD_CLIENT
327
+ });
328
+ }).end();
329
+ chain.entry(backgroundEntry).add({
330
+ layer: LAYERS.BACKGROUND,
331
+ import: backgroundImports,
332
+ filename: backgroundThreadName
333
+ }).when(enabledHMR, (entry)=>{
334
+ entry.prepend({
335
+ layer: LAYERS.BACKGROUND,
336
+ import: HMR_BACKGROUND_CLIENT
337
+ });
338
+ }).when(enabledHMR || enabledLiveReload, (entry)=>{
339
+ entry.prepend({
340
+ layer: LAYERS.BACKGROUND,
341
+ import: '@lynx-js/webpack-dev-transport/client'
342
+ });
343
+ }).end();
344
+ chain.plugin(`${PLUGIN_NAME_TEMPLATE}-${entryName}`).use(LynxTemplatePlugin, [
345
+ {
346
+ filename: templateFilename,
347
+ ...lazyBundleFilename ? {
348
+ lazyBundleFilename
349
+ } : {},
350
+ intermediate: lynxConfig.resolveIntermediateDir({
351
+ entryName
352
+ }),
353
+ chunks: [
354
+ mainThreadEntry,
355
+ backgroundEntry
356
+ ],
357
+ cssPlugins: [
358
+ CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
359
+ ],
360
+ customCSSInheritanceList,
361
+ debugInfoOutside,
362
+ defaultDisplayLinear,
363
+ defaultOverflowVisible,
364
+ enableAccessibilityElement,
365
+ enableCSSInheritance,
366
+ enableCSSInvalidation,
367
+ enableCSSSelector,
368
+ enableNewGesture,
369
+ enableRemoveCSSScope,
370
+ removeDescendantSelectorScope,
371
+ targetSdkVersion,
372
+ enableA11y
373
+ }
374
+ ]).end();
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
+ }
384
+ if (isLynx) {
385
+ chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
386
+ {
387
+ injectVars (vars) {
388
+ return vars.map((name)=>{
389
+ if ('Component' === name) return '__Component';
390
+ return name;
391
+ });
392
+ },
393
+ targetSdkVersion,
394
+ test: /^(?!.*main-thread(?:\.[A-Fa-f0-9]*)?\.js$).*\.js$/
395
+ }
396
+ ]).end();
397
+ chain.plugin(`${LynxEncodePlugin.name}`).use(LynxEncodePlugin, [
398
+ {}
399
+ ]).end();
400
+ }
401
+ if (isWeb) chain.plugin(`${WebEncodePlugin.name}`).use(WebEncodePlugin, []).end();
402
+ chain.plugin(PLUGIN_NAME_ANGULAR).after(PLUGIN_NAME_TEMPLATE).use(AngularWebpackPlugin, [
403
+ {
404
+ mainThreadChunks,
405
+ enableSSR: options.enableSSR,
406
+ isWeb
407
+ }
408
+ ]);
409
+ });
410
+ };
411
+ const getChunks = (entryName, entryValue)=>{
412
+ const chunks = [
413
+ entryName
414
+ ];
415
+ const imports = [];
416
+ for (const item of entryValue){
417
+ if ('string' == typeof item) {
418
+ imports.push(item);
419
+ continue;
420
+ }
421
+ if (Array.isArray(item)) {
422
+ imports.push(...imports);
423
+ continue;
424
+ }
425
+ const { dependOn } = item;
426
+ if (Array.isArray(item.import)) imports.push(...item.import);
427
+ else imports.push(item.import);
428
+ if (dependOn) if ('string' == typeof dependOn) chunks.unshift(dependOn);
429
+ else chunks.unshift(...dependOn);
430
+ }
431
+ return {
432
+ chunks,
433
+ imports
434
+ };
435
+ };
436
+ const BYTES_IN_KILOBYTE = 1000;
437
+ const calculateBytes = (input, baseline, factor = 1)=>{
438
+ const matches = input.trim().match(/^(\d+(?:\.\d+)?)[ \t]*(%|[kmg]?b)?$/i);
439
+ if (!matches) return NaN;
440
+ const baselineBytes = baseline && calculateBytes(baseline) || 0;
441
+ let value = Number(matches[1]);
442
+ switch(matches[2]?.toLowerCase()){
443
+ case '%':
444
+ value = baselineBytes * value / 100;
445
+ break;
446
+ case 'kb':
447
+ value *= BYTES_IN_KILOBYTE;
448
+ break;
449
+ case 'mb':
450
+ value *= BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE;
451
+ break;
452
+ case 'gb':
453
+ value *= BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE * BYTES_IN_KILOBYTE;
454
+ break;
455
+ }
456
+ if (0 === baselineBytes) return value;
457
+ return baselineBytes + value * factor;
458
+ };
459
+ const calculateThresholds = (budget)=>{
460
+ const thresholds = [];
461
+ if (budget.maximumWarning) thresholds.push({
462
+ limit: calculateBytes(budget.maximumWarning, budget.baseline, 1),
463
+ type: 'maximum',
464
+ severity: 'warning'
465
+ });
466
+ if (budget.maximumError) thresholds.push({
467
+ limit: calculateBytes(budget.maximumError, budget.baseline, 1),
468
+ type: 'maximum',
469
+ severity: 'error'
470
+ });
471
+ if (budget.minimumWarning) thresholds.push({
472
+ limit: calculateBytes(budget.minimumWarning, budget.baseline, -1),
473
+ type: 'minimum',
474
+ severity: 'warning'
475
+ });
476
+ if (budget.minimumError) thresholds.push({
477
+ limit: calculateBytes(budget.minimumError, budget.baseline, -1),
478
+ type: 'minimum',
479
+ severity: 'error'
480
+ });
481
+ if (budget.warning) thresholds.push({
482
+ limit: calculateBytes(budget.warning, budget.baseline, -1),
483
+ type: 'minimum',
484
+ severity: 'warning'
485
+ }, {
486
+ limit: calculateBytes(budget.warning, budget.baseline, 1),
487
+ type: 'maximum',
488
+ severity: 'warning'
489
+ });
490
+ if (budget.error) thresholds.push({
491
+ limit: calculateBytes(budget.error, budget.baseline, -1),
492
+ type: 'minimum',
493
+ severity: 'error'
494
+ }, {
495
+ limit: calculateBytes(budget.error, budget.baseline, 1),
496
+ type: 'maximum',
497
+ severity: 'error'
498
+ });
499
+ return thresholds;
500
+ };
501
+ const formatSize = (size)=>{
502
+ if (size <= 0) return '0 bytes';
503
+ const abbreviations = [
504
+ 'bytes',
505
+ 'kB',
506
+ 'MB',
507
+ 'GB'
508
+ ];
509
+ const index = Math.floor(Math.log(size) / Math.log(BYTES_IN_KILOBYTE));
510
+ const roundedSize = size / Math.pow(BYTES_IN_KILOBYTE, index);
511
+ const fractionDigits = 0 === index ? 0 : 2;
512
+ return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
513
+ };
514
+ const checkThresholds = (thresholds, size, label)=>thresholds.flatMap(({ limit, type, severity })=>{
515
+ if ('maximum' === type) return size <= limit ? [] : [
516
+ {
517
+ severity,
518
+ label,
519
+ message: `${label} exceeded maximum budget. Budget ${formatSize(limit)} was not met by ${formatSize(size - limit)} with a total of ${formatSize(size)}.`
520
+ }
521
+ ];
522
+ return size >= limit ? [] : [
523
+ {
524
+ severity,
525
+ label,
526
+ message: `${label} failed to meet minimum budget. Budget ${formatSize(limit)} was not met by ${formatSize(limit - size)} with a total of ${formatSize(size)}.`
527
+ }
528
+ ];
529
+ });
530
+ const sum = (sizes)=>sizes.reduce((total, { size })=>total + size, 0);
531
+ const calculateSizes = (budget, assets, componentStyles)=>{
532
+ switch(budget.type){
533
+ case 'initial':
534
+ {
535
+ const bundles = assets.filter((asset)=>void 0 !== asset.entryName);
536
+ return bundles.map((asset)=>({
537
+ size: asset.size,
538
+ label: 1 === bundles.length ? 'bundle initial' : `bundle initial (${asset.entryName})`
539
+ }));
540
+ }
541
+ case 'bundle':
542
+ return budget.name ? [
543
+ {
544
+ size: sum(assets.filter((asset)=>asset.entryName === budget.name)),
545
+ label: budget.name
546
+ }
547
+ ] : [];
548
+ case 'all':
549
+ return [
550
+ {
551
+ size: sum(assets),
552
+ label: 'total'
553
+ }
554
+ ];
555
+ case 'allScript':
556
+ return [
557
+ {
558
+ size: sum(assets),
559
+ label: "total scripts"
560
+ }
561
+ ];
562
+ case 'any':
563
+ case 'anyScript':
564
+ return assets.map((asset)=>({
565
+ size: asset.size,
566
+ label: asset.name
567
+ }));
568
+ case 'anyComponentStyle':
569
+ return componentStyles.map((style)=>({
570
+ ...style
571
+ }));
572
+ default:
573
+ return [];
574
+ }
575
+ };
576
+ const checkBudgets = (budgets, output)=>budgets.flatMap((budget)=>calculateSizes(budget, output.assets, output.componentStyles).flatMap(({ size, label })=>checkThresholds(calculateThresholds(budget), size, label)));
577
+ const PLUGIN_NAME = 'lynx:angular:budgets';
578
+ const MAIN_THREAD_ENTRY_SUFFIX = '__main-thread';
579
+ const toBudgetError = (failure)=>{
580
+ const error = new Error(failure.message);
581
+ error.name = 'error' === failure.severity ? 'AngularBudgetError' : 'AngularBudgetWarning';
582
+ error.hideStack = true;
583
+ return error;
584
+ };
585
+ const bundleEntryName = (chunkGroups, entrypoints)=>chunkGroups.map((chunkGroup)=>chunkGroup.name).find((name)=>!!name && entrypoints.has(name) && !name.endsWith(MAIN_THREAD_ENTRY_SUFFIX));
586
+ const collectBudgetAssets = (assets, entryBundles, intermediateDir)=>assets.filter(({ name, info })=>/\.(bundle|js)$/.test(name) && !info.hotModuleReplacement && !(intermediateDir && name.startsWith(`${intermediateDir}/`))).map(({ name, source })=>{
587
+ const entryName = entryBundles.get(name);
588
+ return {
589
+ name,
590
+ size: source.size(),
591
+ ...void 0 === entryName ? {} : {
592
+ entryName
593
+ }
594
+ };
595
+ });
596
+ class AngularBudgetsPlugin {
597
+ #options;
598
+ constructor(options){
599
+ this.#options = options;
600
+ }
601
+ apply(compiler) {
602
+ const { budgets, componentStyleSizes, intermediateDir } = this.#options;
603
+ compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation)=>{
604
+ const entryBundles = new Map();
605
+ LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation).beforeEmit.tap(PLUGIN_NAME, (args)=>{
606
+ const entryName = bundleEntryName(args.chunkGroups, compilation.entrypoints);
607
+ if (void 0 !== entryName) entryBundles.set(args.outputName, entryName);
608
+ return args;
609
+ });
610
+ compilation.hooks.processAssets.tap({
611
+ name: PLUGIN_NAME,
612
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT + 2
613
+ }, ()=>{
614
+ const failures = checkBudgets(budgets, {
615
+ assets: collectBudgetAssets(compilation.getAssets(), entryBundles, intermediateDir),
616
+ componentStyles: [
617
+ ...componentStyleSizes.values()
618
+ ]
619
+ });
620
+ for (const failure of failures)('error' === failure.severity ? compilation.errors : compilation.warnings).push(toBudgetError(failure));
621
+ });
622
+ });
623
+ }
624
+ }
625
+ const applyBudgets = (api, { budgets, componentStyleSizes, underAngularCli })=>{
626
+ if (0 === budgets.length) return;
627
+ if (!underAngularCli && 'dev' === api.context.action) return;
628
+ api.modifyBundlerChain((chain, { environment })=>{
629
+ if ('lynx' !== environment.name) return;
630
+ const lynxConfig = api.useExposed(S_LYNX_CONFIG);
631
+ chain.plugin(PLUGIN_NAME).use(AngularBudgetsPlugin, [
632
+ {
633
+ budgets,
634
+ componentStyleSizes,
635
+ intermediateDir: lynxConfig?.resolveIntermediateDir()
636
+ }
637
+ ]);
638
+ });
78
639
  };
79
640
  const injectLynxSchema = (source)=>{
80
641
  if (source.includes('CUSTOM_ELEMENTS_SCHEMA') || source.includes('NO_ERRORS_SCHEMA')) return source;
@@ -115,68 +676,411 @@ const buildLynxSchemaSourceFileCache = (tsconfig, shouldInclude = ()=>true)=>{
115
676
  fileNames
116
677
  };
117
678
  };
679
+ const componentStyleSizeKey = (containingFile, stylesheetKey)=>`${containingFile}#${stylesheetKey}`;
680
+ const forgetComponentStyleSizes = (componentStyleSizes, containingFile)=>{
681
+ const prefix = componentStyleSizeKey(containingFile, '');
682
+ for (const key of componentStyleSizes.keys())if (key.startsWith(prefix)) componentStyleSizes.delete(key);
683
+ };
684
+ const createTransformStylesheet = (params)=>{
685
+ const { basePath, componentStyleSizes } = params;
686
+ return async (data, containingFile, stylesheetFile, order, className)=>{
687
+ const key = componentStyleSizeKey(containingFile, stylesheetFile ?? `__inline_${order}`);
688
+ if (componentStyleSizes && (className || !componentStyleSizes.has(key))) componentStyleSizes.set(key, {
689
+ label: node_path.relative(basePath, stylesheetFile ?? containingFile).replace(/\\/g, '/'),
690
+ size: Buffer.byteLength(data)
691
+ });
692
+ return data;
693
+ };
694
+ };
118
695
  const generateComponentScopeId = (className, containingFile)=>{
119
696
  const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
120
697
  return `l${hash}`;
121
698
  };
122
- const createTransformStylesheet = (params)=>{
123
- const { basePath, scopedCssCacheDir, componentStylesCache, componentScopeIds, onStylesheetWritten } = params;
124
- return async (data, containingFile, stylesheetFile, order, className)=>{
125
- const resolvedClassName = className ?? 'Component';
126
- const scopeId = generateComponentScopeId(resolvedClassName, containingFile);
127
- let componentStyles = componentStylesCache.get(containingFile);
128
- if (!componentStyles) {
129
- componentStyles = {
130
- imports: [],
131
- inlineStyles: [],
132
- scopeId,
133
- processedFiles: new Map()
134
- };
135
- componentStylesCache.set(containingFile, componentStyles);
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
+ }
136
746
  }
137
- const stylesheetKey = stylesheetFile ?? `__inline_${order}`;
138
- const previousPath = componentStyles.processedFiles.get(stylesheetKey);
139
- if (previousPath && !className) return '';
140
- if (className || !componentScopeIds.has(containingFile)) componentScopeIds.set(containingFile, {
141
- className: resolvedClassName,
142
- scopeId
143
- });
144
- const scopedCss = data;
145
- const writeIfChanged = (filePath, content)=>{
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;
146
767
  try {
147
- if (node_fs.readFileSync(filePath, 'utf-8') === content) return;
148
- } catch {}
149
- node_fs.writeFileSync(filePath, content);
150
- onStylesheetWritten?.(filePath);
151
- };
152
- let scopedPath;
153
- if (stylesheetFile) {
154
- const relName = node_path.relative(basePath, stylesheetFile);
155
- scopedPath = node_path.join(scopedCssCacheDir, `${relName.replace(/[/\\]/g, '__')}.__scoped_${scopeId}.css`);
156
- } else {
157
- const relDir = node_path.relative(basePath, node_path.dirname(containingFile));
158
- scopedPath = node_path.join(scopedCssCacheDir, `${relDir.replace(/[/\\]/g, '__')}__inline_${resolvedClassName}_${order}.__scoped_${scopeId}.css`);
159
- }
160
- writeIfChanged(scopedPath, scopedCss);
161
- if (previousPath) {
162
- const idx = componentStyles.imports.indexOf(previousPath);
163
- if (idx >= 0) componentStyles.imports[idx] = scopedPath;
164
- else componentStyles.imports.push(scopedPath);
165
- if (previousPath !== scopedPath) try {
166
- node_fs.unlinkSync(previousPath);
768
+ current = node_fs.readFileSync(filePath, 'utf-8');
167
769
  } catch {}
168
- } else componentStyles.imports.push(scopedPath);
169
- componentStyles.processedFiles.set(stylesheetKey, scopedPath);
170
- return '';
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
171
784
  };
172
785
  };
173
786
  const isLynxUnknownElementMessage = (text)=>(text?.includes('is not a known element') || text?.includes("isn't a known property of")) ?? false;
787
+ const filterAngularDiagnostics = (diagnostics)=>({
788
+ errors: (diagnostics.errors ?? []).filter((message)=>!isLynxUnknownElementMessage(message.text)),
789
+ warnings: (diagnostics.warnings ?? []).filter((message)=>!isLynxUnknownElementMessage(message.text))
790
+ });
791
+ const formatLocation = ({ file, line, column })=>{
792
+ if (!file) return;
793
+ if (void 0 === line) return file;
794
+ return `${file}:${line}:${(column ?? 0) + 1}`;
795
+ };
796
+ const formatCodeFrame = ({ line, column, length, lineText })=>{
797
+ if (void 0 === line || void 0 === lineText) return;
798
+ const gutter = `${line} | `;
799
+ const caretIndent = ' '.repeat(gutter.length - 2) + '| ';
800
+ const caret = ' '.repeat(column ?? 0) + '^'.repeat(Math.max(1, length ?? 1));
801
+ return `${gutter}${lineText}\n${caretIndent}${caret}`;
802
+ };
803
+ const toAngularDiagnosticError = (message, severity)=>{
804
+ const sections = [
805
+ message.text ?? 'Unknown Angular compiler diagnostic'
806
+ ];
807
+ const codeFrame = message.location && formatCodeFrame(message.location);
808
+ if (codeFrame) sections.push(codeFrame);
809
+ for (const note of message.notes ?? []){
810
+ const noteLocation = note.location && formatLocation(note.location);
811
+ sections.push([
812
+ noteLocation,
813
+ note.text
814
+ ].filter((part)=>part).join(': '));
815
+ }
816
+ const error = new Error(sections.join('\n\n'));
817
+ error.name = 'error' === severity ? 'AngularCompilerError' : 'AngularCompilerWarning';
818
+ const file = message.location && formatLocation(message.location);
819
+ if (file) error.file = file;
820
+ error.hideStack = true;
821
+ return error;
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
+ };
174
927
  const seedCompilationSourceFiles = (compilation, sourceFileCache)=>{
175
928
  const { sourceFiles } = compilation;
176
929
  if (!(sourceFiles instanceof Map)) return false;
177
930
  for (const [fileName, sourceFile] of sourceFileCache)sourceFiles.set(fileName, sourceFile);
178
931
  return true;
179
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
+ };
180
1084
  const MAIN_THREAD_DIRECTIVE = 'main thread';
181
1085
  const MAIN_THREAD_DIRECTIVE_ALT = 'main-thread';
182
1086
  const transformWorklets = (code, filename)=>{
@@ -223,10 +1127,10 @@ const isAlreadyWrapped = (node)=>{
223
1127
  return false;
224
1128
  };
225
1129
  const buildTransformedCode = (params)=>{
226
- const { resourcePath, componentStyles, scopeInfo } = params;
1130
+ const { resourcePath, stylesheets, root, hot } = params;
227
1131
  let code = params.code;
228
- if (componentStyles) {
229
- const { imports } = componentStyles;
1132
+ if (stylesheets) {
1133
+ const { imports } = stylesheets;
230
1134
  let importsString = '';
231
1135
  for(let i = 0; i < imports.length; ++i){
232
1136
  let relativeImport = node_path.relative(node_path.dirname(resourcePath), imports[i]);
@@ -235,8 +1139,19 @@ const buildTransformedCode = (params)=>{
235
1139
  }
236
1140
  code = importsString + code;
237
1141
  }
238
- if (scopeInfo) code += `\n;${scopeInfo.className}.ɵcmp.id = '${scopeInfo.scopeId}';\n`;
239
- return transformWorklets(code, resourcePath);
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
+ });
240
1155
  };
241
1156
  const applyAngularConfig = (api, buildOptions)=>{
242
1157
  const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -253,9 +1168,9 @@ const applyAngularConfig = (api, buildOptions)=>{
253
1168
  ];
254
1169
  else config.source.preEntry ??= [];
255
1170
  config.source.preEntry.push(node_path.resolve(__dirname, './polyfills'));
256
- if (buildOptions.i18n.hasDefinedSourceLocale) config.source.preEntry.push('@angular/localize/init');
257
- const polyfills = buildOptions.polyfills;
258
- if (polyfills) config.source.preEntry.push(...polyfills);
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);
259
1174
  config.source.preEntry.push(...buildOptions.styles);
260
1175
  config.source.tsconfigPath = buildOptions.tsconfig;
261
1176
  config.source.define ??= {};
@@ -264,12 +1179,85 @@ const applyAngularConfig = (api, buildOptions)=>{
264
1179
  if (isProd) config.source.define.ngDevMode = false;
265
1180
  });
266
1181
  };
1182
+ const build_internals_require = createRequire(import.meta.url);
1183
+ const angularBuildRoot = node_path.dirname(build_internals_require.resolve('@angular/build/package.json'));
1184
+ const loadAngularBuildInternal = (subpath)=>build_internals_require(node_path.join(angularBuildRoot, subpath));
1185
+ const { createAngularCompilation: createAngularCompilation, DiagnosticModes: DiagnosticModes } = loadAngularBuildInternal('src/tools/angular/compilation');
1186
+ const { JavaScriptTransformer: JavaScriptTransformer } = loadAngularBuildInternal("src/tools/javascript-transformer");
1187
+ const { ComponentStylesheetBundler: ComponentStylesheetBundler } = loadAngularBuildInternal('src/tools/esbuild/angular/component-stylesheets');
267
1188
  const isPresent = (variable)=>'string' == typeof variable && '' !== variable;
268
1189
  const isDisabled = (variable)=>'0' === variable || 'false' === variable.toLowerCase();
269
1190
  const maxWorkersVariable = process.env.NG_BUILD_MAX_WORKERS;
270
1191
  const maxWorkers = isPresent(maxWorkersVariable) ? +maxWorkersVariable : Math.min(4, Math.max(availableParallelism() - 1, 1));
271
1192
  const typeCheckingVariable = process.env.NG_BUILD_TYPE_CHECK;
272
1193
  const useTypeChecking = !isPresent(typeCheckingVariable) || !isDisabled(typeCheckingVariable);
1194
+ /**
1195
+ * @license
1196
+ * Copyright Google LLC All Rights Reserved.
1197
+ *
1198
+ * Use of this source code is governed by an MIT-style license that can be
1199
+ * found in the LICENSE file at https://angular.dev/license
1200
+ */ const findUp = (names, from)=>{
1201
+ const namesList = Array.isArray(names) ? names : [
1202
+ names
1203
+ ];
1204
+ const root = __rspack_external_node_path_c5b9b54f.parse(from).root;
1205
+ let currentDir = from;
1206
+ while(currentDir && currentDir !== root){
1207
+ for (const name of namesList){
1208
+ const p = __rspack_external_node_path_c5b9b54f.join(currentDir, name);
1209
+ if (existsSync(p)) return p;
1210
+ }
1211
+ currentDir = __rspack_external_node_path_c5b9b54f.dirname(currentDir);
1212
+ }
1213
+ return null;
1214
+ };
1215
+ const configNames = [
1216
+ 'angular.json',
1217
+ '.angular.json'
1218
+ ];
1219
+ const getAngularWorkspace = async (from = process.cwd())=>{
1220
+ const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
1221
+ const workspaceFile = findUp(configNames, from);
1222
+ if (!workspaceFile) throw new Error("couldn't find workspace file");
1223
+ const { workspace } = await workspaces.readWorkspace(workspaceFile, host);
1224
+ return {
1225
+ basePath: node_path.dirname(workspaceFile),
1226
+ workspace
1227
+ };
1228
+ };
1229
+ const findProjectByPath = (workspace, location, basePath)=>{
1230
+ const isInside = (base, potential)=>{
1231
+ const absoluteBase = node_path.resolve(basePath, base);
1232
+ const absolutePotential = node_path.resolve(basePath, potential);
1233
+ const relativePotential = node_path.relative(absoluteBase, absolutePotential);
1234
+ if (!relativePotential.startsWith('..') && !node_path.isAbsolute(relativePotential)) return true;
1235
+ return false;
1236
+ };
1237
+ const projects = Array.from(workspace.projects).map(([name, project])=>[
1238
+ project.root,
1239
+ name
1240
+ ]).filter((tuple)=>isInside(tuple[0], location)).sort((a, b)=>b[0].length - a[0].length);
1241
+ if (0 === projects.length) return null;
1242
+ if (projects.length > 1) {
1243
+ const found = new Set();
1244
+ const sameRoots = projects.filter((v)=>{
1245
+ if (!found.has(v[0])) {
1246
+ found.add(v[0]);
1247
+ return false;
1248
+ }
1249
+ return true;
1250
+ });
1251
+ if (sameRoots.length > 0) return null;
1252
+ }
1253
+ return projects[0][1];
1254
+ };
1255
+ const getProjectByCwd = (workspace, basePath)=>{
1256
+ if (1 === workspace.projects.size) return Array.from(workspace.projects.keys())[0];
1257
+ const project = findProjectByPath(workspace, process.cwd(), basePath);
1258
+ if (project) return project;
1259
+ return null;
1260
+ };
273
1261
  /**
274
1262
  * @license
275
1263
  * Copyright Google LLC All Rights Reserved.
@@ -291,7 +1279,7 @@ const normalizeCacheOptions = (projectMetadata, workspaceRoot)=>{
291
1279
  cacheEnabled = !isCI;
292
1280
  break;
293
1281
  }
294
- const cacheBasePath = resolve(workspaceRoot, path);
1282
+ const cacheBasePath = external_node_path_resolve(workspaceRoot, path);
295
1283
  return {
296
1284
  enabled: cacheEnabled,
297
1285
  basePath: cacheBasePath,
@@ -338,29 +1326,37 @@ const normalizeOptimization = (optimization = true)=>{
338
1326
  };
339
1327
  };
340
1328
  const readTarget = (project, target = 'build')=>project.targets.get(target);
341
- const readBuildOptions = async (project, basePath, configurationName)=>{
342
- const workspaceRoot = basePath;
1329
+ const resolveIndexPath = (index, project, workspaceRoot)=>{
1330
+ if ('boolean' == typeof index) return;
1331
+ if ('string' == typeof index) return node_path.join(workspaceRoot, index);
1332
+ if (index && 'object' == typeof index) return node_path.join(workspaceRoot, index.input);
1333
+ const sourceRoot = project.sourceRoot ?? node_path.join(project.root ?? '', 'src');
1334
+ return node_path.join(workspaceRoot, sourceRoot, 'index.html');
1335
+ };
1336
+ const resolveOutputPath = (outputPath, workspaceRoot)=>{
1337
+ if ('string' == typeof outputPath) return node_path.join(workspaceRoot, outputPath);
1338
+ if (outputPath && 'object' == typeof outputPath) return node_path.join(workspaceRoot, outputPath.base);
1339
+ };
1340
+ const readBuildTargetOptions = (project, configurationName)=>{
343
1341
  const target = readTarget(project);
344
1342
  if (!target) throw new Error("couldn't find target");
345
1343
  const resolvedConfigurationName = configurationName ?? target.defaultConfiguration;
346
- const buildOptions = target?.options;
347
- if (!buildOptions) throw new Error(`No build options found for the "${configurationName ?? 'build'}" target in angular.json. Make sure the target has an "options" section with at least "browser", "tsConfig", and "index" defined.`);
348
- const configurations = target.configurations;
349
- if (configurations) {
350
- for(const name in configurations)if (name === resolvedConfigurationName) {
351
- Object.assign(buildOptions, {
352
- ...configurations[name]
353
- });
354
- break;
355
- }
356
- }
357
- node_assert(!!buildOptions, 'build options is undefined');
1344
+ const buildOptions = target.options;
1345
+ if (!buildOptions) throw new Error(`No build options found for the "${configurationName ?? 'build'}" target in angular.json. Make sure the target has an "options" section with at least "browser" and "tsConfig" defined.`);
1346
+ return {
1347
+ ...buildOptions,
1348
+ ...resolvedConfigurationName ? target.configurations?.[resolvedConfigurationName] : void 0
1349
+ };
1350
+ };
1351
+ const readBuildOptions = async (project, basePath, configurationName)=>normalizeBuildOptions(readBuildTargetOptions(project, configurationName), project, basePath);
1352
+ const normalizeBuildOptions = async (buildOptions, project, basePath)=>{
1353
+ const workspaceRoot = basePath;
358
1354
  const aot = buildOptions.aot ?? true;
359
1355
  const tsconfig = node_path.join(workspaceRoot, buildOptions.tsConfig);
360
1356
  const optimizationOptions = normalizeOptimization(buildOptions.optimization);
361
1357
  const sourcemapOptions = normalizeSourceMaps(buildOptions.sourceMap ?? false);
362
1358
  const browser = node_path.join(workspaceRoot, buildOptions.browser);
363
- const index = node_path.join(workspaceRoot, buildOptions.index);
1359
+ const index = resolveIndexPath(buildOptions.index, project, workspaceRoot);
364
1360
  let polyfills = buildOptions.polyfills;
365
1361
  polyfills = void 0 === polyfills || Array.isArray(polyfills) ? polyfills : [
366
1362
  polyfills
@@ -387,7 +1383,7 @@ const readBuildOptions = async (project, basePath, configurationName)=>{
387
1383
  const cacheOptions = normalizeCacheOptions(buildOptions.projectMetadata, workspaceRoot);
388
1384
  const inlineStyleLanguage = buildOptions.inlineStyleLanguage;
389
1385
  const styles = (buildOptions.styles ?? []).map((stylePath)=>node_path.join(workspaceRoot, stylePath));
390
- const outputPath = node_path.join(workspaceRoot, buildOptions.outputPath);
1386
+ const outputPath = resolveOutputPath(buildOptions.outputPath, workspaceRoot);
391
1387
  const i18n = normalizeI18nOptions(project.extensions['i18n']);
392
1388
  const i18nMissingTranslation = buildOptions.i18nMissingTranslation ?? 'warning';
393
1389
  const normalizedOptions = {
@@ -408,7 +1404,8 @@ const readBuildOptions = async (project, basePath, configurationName)=>{
408
1404
  outputHashing,
409
1405
  styles,
410
1406
  i18n,
411
- i18nMissingTranslation
1407
+ i18nMissingTranslation,
1408
+ budgets: buildOptions.budgets ?? []
412
1409
  };
413
1410
  return normalizedOptions;
414
1411
  };
@@ -432,73 +1429,20 @@ const normalizeI18nOptions = (raw)=>{
432
1429
  hasDefinedSourceLocale
433
1430
  };
434
1431
  };
435
- /**
436
- * @license
437
- * Copyright Google LLC All Rights Reserved.
438
- *
439
- * Use of this source code is governed by an MIT-style license that can be
440
- * found in the LICENSE file at https://angular.dev/license
441
- */ const findUp = (names, from)=>{
442
- const namesList = Array.isArray(names) ? names : [
443
- names
444
- ];
445
- const root = __rspack_external_node_path_c5b9b54f.parse(from).root;
446
- let currentDir = from;
447
- while(currentDir && currentDir !== root){
448
- for (const name of namesList){
449
- const p = __rspack_external_node_path_c5b9b54f.join(currentDir, name);
450
- if (existsSync(p)) return p;
451
- }
452
- currentDir = __rspack_external_node_path_c5b9b54f.dirname(currentDir);
453
- }
454
- return null;
455
- };
456
- const configNames = [
457
- 'angular.json',
458
- '.angular.json'
459
- ];
460
- const findWorkspaceFile = ()=>findUp(configNames, process.cwd());
461
- const getAngularWorkspace = async ()=>{
462
- const host = workspaces.createWorkspaceHost(new NodeJsSyncHost());
463
- const workspaceFile = findWorkspaceFile();
464
- if (!workspaceFile) throw new Error("couldn't find workspace file");
465
- const { workspace } = await workspaces.readWorkspace(workspaceFile, host);
1432
+ const resolvePrimaryBuild = async ({ workspace, basePath, pages, cli })=>{
1433
+ let projectName;
1434
+ if (pages) {
1435
+ const candidates = 'all' === pages ? Array.from(workspace.projects.entries()).filter(([, project])=>'application' === project.extensions['projectType']).map(([name])=>name) : pages;
1436
+ projectName = cli && candidates.includes(cli.project) ? cli.project : candidates[0] ?? null;
1437
+ } else projectName = cli?.project ?? getProjectByCwd(workspace, basePath);
1438
+ if (!projectName) throw new Error("couldn't find the project");
1439
+ const project = workspace.projects.get(projectName);
1440
+ if (!project) throw new Error(`Project "${projectName}" not found in workspace`);
1441
+ const buildOptions = cli?.project === projectName ? await normalizeBuildOptions(cli.buildOptions, project, basePath) : await readBuildOptions(project, basePath);
466
1442
  return {
467
- basePath: node_path.dirname(workspaceFile),
468
- workspace
469
- };
470
- };
471
- const findProjectByPath = (workspace, location, basePath)=>{
472
- const isInside = (base, potential)=>{
473
- const absoluteBase = node_path.resolve(basePath, base);
474
- const absolutePotential = node_path.resolve(basePath, potential);
475
- const relativePotential = node_path.relative(absoluteBase, absolutePotential);
476
- if (!relativePotential.startsWith('..') && !node_path.isAbsolute(relativePotential)) return true;
477
- return false;
478
- };
479
- const projects = Array.from(workspace.projects).map(([name, project])=>[
480
- project.root,
481
- name
482
- ]).filter((tuple)=>isInside(tuple[0], location)).sort((a, b)=>b[0].length - a[0].length);
483
- if (0 === projects.length) return null;
484
- if (projects.length > 1) {
485
- const found = new Set();
486
- const sameRoots = projects.filter((v)=>{
487
- if (!found.has(v[0])) {
488
- found.add(v[0]);
489
- return false;
490
- }
491
- return true;
492
- });
493
- if (sameRoots.length > 0) return null;
494
- }
495
- return projects[0][1];
496
- };
497
- const getProjectByCwd = (workspace, basePath)=>{
498
- if (1 === workspace.projects.size) return Array.from(workspace.projects.keys())[0];
499
- const project = findProjectByPath(workspace, process.cwd(), basePath);
500
- if (project) return project;
501
- return null;
1443
+ projectName,
1444
+ buildOptions
1445
+ };
502
1446
  };
503
1447
  const resolvePages = (workspace, basePath, pages)=>{
504
1448
  const projectNames = 'all' === pages ? Array.from(workspace.projects.entries()).filter(([, def])=>'application' === def.extensions['projectType']).map(([name])=>name) : pages;
@@ -744,8 +1688,9 @@ const reportLynxDiagnostics = (diagnostics)=>{
744
1688
  for (const w of cssWarnings)console.warn(` ${w.message}`);
745
1689
  }
746
1690
  };
747
- const applyAngularRules = async (api, pluginOptions)=>{
748
- const { basePath, workspace } = await getAngularWorkspace();
1691
+ const applyAngularRules = async (api, pluginOptions, backgroundEntries, appEntries)=>{
1692
+ const cli = getAngularCliContext(api);
1693
+ const { basePath, workspace } = await getAngularWorkspace(cli?.workspaceRoot);
749
1694
  if (pluginOptions.pages) {
750
1695
  const pages = resolvePages(workspace, basePath, pluginOptions.pages);
751
1696
  api.modifyRsbuildConfig((config)=>{
@@ -756,12 +1701,19 @@ const applyAngularRules = async (api, pluginOptions)=>{
756
1701
  ]));
757
1702
  });
758
1703
  }
759
- const primaryProjectName = pluginOptions.pages ? (Array.isArray(pluginOptions.pages) ? pluginOptions.pages[0] : Array.from(workspace.projects.entries()).find(([, def])=>'application' === def.extensions['projectType'])?.[0]) ?? null : getProjectByCwd(workspace, basePath);
760
- if (!primaryProjectName) throw new Error("couldn't find the project");
761
- const projectDefinition = workspace.projects.get(primaryProjectName);
762
- if (!projectDefinition) throw new Error(`Project "${primaryProjectName}" not found in workspace`);
763
- const buildOptions = await readBuildOptions(projectDefinition, basePath);
1704
+ const { buildOptions } = await resolvePrimaryBuild({
1705
+ workspace,
1706
+ basePath,
1707
+ pages: pluginOptions.pages,
1708
+ cli
1709
+ });
764
1710
  applyAngularConfig(api, buildOptions);
1711
+ const componentStyleSizes = new Map();
1712
+ applyBudgets(api, {
1713
+ budgets: buildOptions.budgets,
1714
+ componentStyleSizes,
1715
+ underAngularCli: void 0 !== cli
1716
+ });
765
1717
  const sourcemap = !!(!!buildOptions.sourcemapOptions.scripts && (buildOptions.sourcemapOptions.hidden ? 'external' : true));
766
1718
  const thirdPartySourcemaps = buildOptions.sourcemapOptions.vendor;
767
1719
  const advancedOptimizations = buildOptions.advancedOptimizations;
@@ -776,21 +1728,23 @@ const applyAngularRules = async (api, pluginOptions)=>{
776
1728
  const tsconfig = buildOptions.tsconfig;
777
1729
  const compilation = await createAngularCompilation(false, aot);
778
1730
  const typeScriptFileCache = new Map();
779
- const scopedCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
780
- node_fs.mkdirSync(scopedCssCacheDir, {
1731
+ const componentCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
1732
+ node_fs.mkdirSync(componentCssCacheDir, {
781
1733
  recursive: true
782
1734
  });
783
- const componentStylesCache = new Map();
784
- const componentScopeIds = new Map();
1735
+ const moduleStylesheets = new Map();
785
1736
  let componentResourceDependencies = new Map();
786
1737
  const writtenStylesheets = new Set();
787
1738
  let knownSourceFiles = new Set();
1739
+ const backgroundModules = new Set();
1740
+ let angularInputs = new Set();
1741
+ let angularDiagnostics = {
1742
+ errors: [],
1743
+ warnings: []
1744
+ };
788
1745
  const transformStylesheet = createTransformStylesheet({
789
1746
  basePath,
790
- scopedCssCacheDir,
791
- componentStylesCache,
792
- componentScopeIds,
793
- onStylesheetWritten: (filePath)=>writtenStylesheets.add(filePath)
1747
+ componentStyleSizes
794
1748
  });
795
1749
  const compileAngular = async (changedFiles)=>{
796
1750
  const { sourceFileCache, fileNames } = buildLynxSchemaSourceFileCache(tsconfig, changedFiles && ((file)=>changedFiles.has(node_path.normalize(file)) || !knownSourceFiles.has(file)));
@@ -826,30 +1780,50 @@ const applyAngularRules = async (api, pluginOptions)=>{
826
1780
  try {
827
1781
  for (const { filename, contents } of (await compilation.emitAffectedFiles())){
828
1782
  const file = node_path.normalize(filename);
829
- if (typeScriptFileCache.get(file) !== contents) changedOutputs.add(file);
830
- typeScriptFileCache.set(file, contents);
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);
831
1799
  }
832
1800
  } catch (error) {
833
1801
  throw new Error(`Angular failed to emit compiled output for "${tsconfig}". Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
834
1802
  }
835
1803
  for (const file of writtenStylesheets)changedOutputs.add(file);
836
- for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) typeScriptFileCache.delete(file);
1804
+ for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) {
1805
+ typeScriptFileCache.delete(file);
1806
+ backgroundModules.delete(file);
1807
+ moduleStylesheets.delete(file);
1808
+ forgetComponentStyleSizes(componentStyleSizes, file);
1809
+ }
837
1810
  reportLynxDiagnostics([
838
1811
  ...scanCompiledOutputForHtmlElements(typeScriptFileCache),
839
1812
  ...scanCompiledOutputForStructuralIssues(typeScriptFileCache),
840
1813
  ...scanSourcesForUnsupportedPatterns(fileNames),
841
1814
  ...scanSourcesForUnsupportedCss(fileNames)
842
1815
  ]);
843
- const diagnostics = await compilation.diagnoseFiles(useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic);
844
- const actionableErrors = diagnostics.errors?.filter((e)=>!isLynxUnknownElementMessage(e.text));
845
- const actionableWarnings = diagnostics.warnings?.filter((w)=>!isLynxUnknownElementMessage(w.text));
846
- if (actionableErrors?.length || actionableWarnings?.length) console.log({
847
- errors: actionableErrors,
848
- warnings: actionableWarnings
849
- });
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
+ ]);
850
1824
  return {
851
1825
  changedOutputs,
852
- inputFiles: result.referencedFiles.map((file)=>node_path.normalize(file))
1826
+ inputFiles
853
1827
  };
854
1828
  };
855
1829
  const coordinator = createAngularRebuildCoordinator({
@@ -861,425 +1835,293 @@ const applyAngularRules = async (api, pluginOptions)=>{
861
1835
  compiler
862
1836
  ];
863
1837
  for (const environmentCompiler of compilers)if (environmentCompiler.name) compilersByEnvironment.set(environmentCompiler.name, environmentCompiler);
1838
+ compilers[0]?.hooks.thisCompilation.tap('lynx:angular:diagnostics', (rspackCompilation)=>{
1839
+ rspackCompilation.errors.push(...angularDiagnostics.errors.map((message)=>toAngularDiagnosticError(message, 'error')));
1840
+ rspackCompilation.warnings.push(...angularDiagnostics.warnings.map((message)=>toAngularDiagnosticError(message, 'warning')));
1841
+ });
864
1842
  });
865
1843
  let compilationClosed = false;
866
1844
  api.onBeforeEnvironmentCompile(async ({ environment, isWatch })=>{
867
1845
  const compiler = compilersByEnvironment.get(environment.name);
868
- const outdatedFiles = await coordinator.prepare(environment.name, [
1846
+ const changedFiles = [
869
1847
  ...compiler?.modifiedFiles ?? [],
870
1848
  ...compiler?.removedFiles ?? []
871
- ]);
872
- if (compiler && outdatedFiles.size > 0) compiler.modifiedFiles = new Set([
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([
873
1857
  ...compiler.modifiedFiles ?? [],
874
- ...outdatedFiles
1858
+ ...outdatedFiles,
1859
+ ...changedEntries
875
1860
  ]);
876
1861
  if (!isWatch && !compilationClosed) {
877
1862
  compilationClosed = true;
878
1863
  await compilation.close?.();
879
1864
  }
880
1865
  });
881
- api.transform({
882
- test: /\.[cm]?[jt]sx?$/
883
- }, async (context)=>{
884
- const isJs = /\.[cm]?js$/.test(context.resourcePath);
885
- if (isJs) {
886
- const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
887
- skipLinker: false,
888
- sideEffects: ()=>Promise.resolve(false)
889
- });
890
- return {
891
- code: transformWorklets(Buffer.from(contents).toString(), context.resourcePath)
892
- };
893
- }
894
- const content = typeScriptFileCache.get(context.resourcePath);
895
- if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
896
- for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
897
- const code = 'string' == typeof content ? content : Buffer.from(content).toString();
898
- return {
899
- code: buildTransformedCode({
900
- code,
901
- resourcePath: context.resourcePath,
902
- componentStyles: componentStylesCache.get(context.resourcePath),
903
- scopeInfo: componentScopeIds.get(context.resourcePath)
904
- })
905
- };
906
- });
907
- };
908
- const LAYERS = {
909
- BACKGROUND: 'background',
910
- MAIN_THREAD: 'main'
911
- };
912
- const applyLayers = (api)=>{
913
- api.modifyBundlerChain((chain)=>{
914
- chain.module.rule("typescript").oneOf(LAYERS.BACKGROUND).layer(LAYERS.BACKGROUND).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
915
- jsc: {
916
- target: 'es2015',
917
- parser: {
918
- syntax: "typescript"
919
- }
920
- }
921
- }).end();
922
- 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({
923
- jsc: {
924
- target: 'es2019',
925
- parser: {
926
- syntax: "typescript"
927
- }
928
- }
929
- }).end();
930
- });
931
- };
932
- const applyCSS = (api, options)=>{
933
- const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
934
- api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
935
- output: {
936
- injectStyles: false
937
- }
938
- }));
939
- const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
940
- api.modifyBundlerChain(async (chain, { CHAIN_ID, environment })=>{
941
- const { CssExtractRspackPlugin } = await import("@lynx-js/css-extract-webpack-plugin");
942
- const removeLightningCSS = (rule)=>{
943
- if (rule.uses.has(CHAIN_ID.USE.LIGHTNINGCSS) && 'lynx' === environment.name) rule.uses.delete(CHAIN_ID.USE.LIGHTNINGCSS);
944
- };
945
- const cssRules = [
946
- CHAIN_ID.RULE.CSS,
947
- CHAIN_ID.RULE.SASS,
948
- CHAIN_ID.RULE.LESS,
949
- CHAIN_ID.RULE.STYLUS
950
- ];
951
- for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
952
- const rule = chain.module.rule(ruleName);
953
- const mainRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_MAIN : ruleName;
954
- const mainRule = rule.oneOf(mainRuleName);
955
- const parentRuleEntries = rule.entries();
956
- removeLightningCSS(mainRule);
957
- mainRule.issuerLayer(LAYERS.BACKGROUND).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
958
- const uses = mainRule.uses.entries() ?? {};
959
- const ruleEntries = mainRule.entries();
960
- const cssLoader = uses[CHAIN_ID.USE.CSS];
961
- if (!cssLoader) continue;
962
- const cssLoaderRule = cssLoader.entries();
963
- const mainThreadLayerRule = chain.module.rule(`${ruleName}:${LAYERS.MAIN_THREAD}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.MAIN_THREAD);
964
- if (void 0 !== parentRuleEntries.dependency) mainThreadLayerRule.merge({
965
- dependency: parentRuleEntries.dependency
966
- });
967
- mainThreadLayerRule.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();
968
- }
969
- for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
970
- const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
971
- removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
972
- }
973
- if ('lynx' === environment.name && chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
974
- const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
975
- fontRule.oneOfs.clear();
976
- fontRule.type('asset/inline');
977
- }
978
- chain.plugin(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT).tap(([options])=>[
979
- {
980
- ...options,
981
- enableRemoveCSSScope,
982
- enableCSSSelector,
983
- enableCSSInvalidation,
984
- targetSdkVersion,
985
- cssPlugins: [
986
- CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
987
- ]
988
- }
989
- ]).init((_, args)=>new CssExtractRspackPlugin(...args)).end().end();
990
- chain.module.when(void 0 === enableRemoveCSSScope, (module)=>module.rule('lynx.css.scoped').test(/\.css$/).resourceQuery({
991
- and: [
992
- /cssId/,
993
- {
994
- not: /common/
995
- }
996
- ]
997
- }).sideEffects(false));
998
- });
999
- };
1000
- const normalizeCssLoaderOptions = (options, exportOnlyLocals)=>{
1001
- if (options.modules && exportOnlyLocals) {
1002
- let { modules } = options;
1003
- modules = true === modules ? {
1004
- exportOnlyLocals: true
1005
- } : 'string' == typeof modules ? {
1006
- mode: modules,
1007
- exportOnlyLocals: true
1008
- } : {
1009
- ...modules,
1010
- exportOnlyLocals: true
1011
- };
1012
- return {
1013
- ...options,
1014
- modules
1015
- };
1016
- }
1017
- return options;
1018
- };
1019
- const createLynxProcessEvalResultRuntimeModule = (webpack)=>class extends webpack.RuntimeModule {
1020
- constructor(){
1021
- super('Lynx process eval result', webpack.RuntimeModule.STAGE_ATTACH);
1022
- }
1023
- generate() {
1024
- const chunk = this.chunk;
1025
- const compilation = this.compilation;
1026
- if (!chunk || !compilation) return '';
1027
- const chunkGroup = chunk._groupsIterable?.[0];
1028
- const modules = compilation.chunkGraph?.getChunkModules(chunk) ?? [];
1029
- modules.sort((a, b)=>{
1030
- const aIdx = chunkGroup?.getModulePreOrderIndex(a) ?? 0;
1031
- const bIdx = chunkGroup?.getModulePreOrderIndex(b) ?? 0;
1032
- return aIdx - bIdx;
1033
- });
1034
- const sortedIds = modules.map((m)=>compilation.chunkGraph.getModuleId(m)).filter((id)=>null != id);
1035
- const moduleOrder = JSON.stringify(sortedIds);
1036
- return `
1037
- ${RuntimeGlobals.lynxProcessEvalResult} = function (result, schema) {
1038
- var chunk = result && result(schema);
1039
- if (chunk && chunk.ids && chunk.modules) {
1040
- ${webpack.RuntimeGlobals.externalInstallChunk}(chunk);
1041
- var moduleOrder = ${moduleOrder};
1042
- for (var i = 0; i < moduleOrder.length; i++) {
1043
- if (chunk.modules[moduleOrder[i]]) {
1044
- ${webpack.RuntimeGlobals.require}(moduleOrder[i]);
1045
- }
1046
- }
1047
- return chunk;
1048
- }
1049
- return chunk
1050
- }
1051
- `;
1052
- }
1053
- };
1054
- class AngularWebpackPlugin {
1055
- options;
1056
- constructor(options){
1057
- this.options = options;
1058
- }
1059
- static defaultOptions = Object.freeze({
1060
- disableCreateSelectorQueryIncompatibleWarning: false,
1061
- firstScreenSyncTiming: 'immediately',
1062
- enableSSR: false,
1063
- isWeb: false,
1064
- mainThreadChunks: [],
1065
- extractStr: false,
1066
- experimental_isLazyBundle: false
1067
- });
1068
- apply(compiler) {
1069
- const options = Object.assign({}, AngularWebpackPlugin.defaultOptions, this.options);
1070
- if (options.enableSSR && 'jsReady' !== options.firstScreenSyncTiming) options.firstScreenSyncTiming = 'jsReady';
1071
- const { DefinePlugin, EnvironmentPlugin } = compiler.webpack;
1072
- new EnvironmentPlugin({
1073
- DEBUG: null
1074
- }).apply(compiler);
1075
- new DefinePlugin({
1076
- __DEV__: JSON.stringify('development' === compiler.options.mode),
1077
- __PROFILE__: JSON.stringify('development' === compiler.options.mode),
1078
- __EXTRACT_STR__: JSON.stringify(Boolean(options.extractStr)),
1079
- __FIRST_SCREEN_SYNC_TIMING__: JSON.stringify(options.firstScreenSyncTiming),
1080
- __ENABLE_SSR__: JSON.stringify(options.enableSSR),
1081
- __WEB__: JSON.stringify(Boolean(options.isWeb)),
1082
- __DISABLE_CREATE_SELECTOR_QUERY_INCOMPATIBLE_WARNING__: JSON.stringify(options.disableCreateSelectorQueryIncompatibleWarning)
1083
- }).apply(compiler);
1084
- compiler.hooks.thisCompilation.tap(this.constructor.name, (compilation)=>{
1085
- const onceForChunkSet = new WeakSet();
1086
- compilation.hooks.runtimeRequirementInTree.for(compiler.webpack.RuntimeGlobals.ensureChunkHandlers).tap('VanillaWebpackPlugin', (chunk, runtimeRequirements)=>{
1087
- runtimeRequirements.add(RuntimeGlobals.lynxProcessEvalResult);
1088
- });
1089
- compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.lynxProcessEvalResult).tap('VanillaWebpackPlugin', (chunk)=>{
1090
- if (onceForChunkSet.has(chunk)) return;
1091
- onceForChunkSet.add(chunk);
1092
- const LynxProcessEvalResultRuntimeModule = createLynxProcessEvalResultRuntimeModule(compiler.webpack);
1093
- compilation.addRuntimeModule(chunk, new LynxProcessEvalResultRuntimeModule());
1094
- });
1095
- compilation.hooks.processAssets.tap({
1096
- name: this.constructor.name,
1097
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
1098
- }, ()=>{
1099
- for (const name of options.mainThreadChunks ?? [])this.#updateMainThreadInfo(compilation, name);
1100
- for (const cg of compilation.chunkGroups.filter((cg)=>!cg.isInitial()).filter((cg)=>cg.origins.every((origin)=>origin.module?.layer === LAYERS.MAIN_THREAD))){
1101
- const files = cg.getFiles();
1102
- for (const name of files.filter((name)=>name.endsWith('.js')))this.#updateMainThreadInfo(compilation, name);
1103
- }
1104
- });
1105
- {
1106
- const { ConcatSource } = compiler.webpack.sources;
1107
- const mainThreadChunkSet = new Set(options.mainThreadChunks ?? []);
1108
- compilation.hooks.processAssets.tap({
1109
- name: `${this.constructor.name}:threadGlobals`,
1110
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
1111
- }, ()=>{
1112
- for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js'))){
1113
- const isMainThread = mainThreadChunkSet.has(name);
1114
- const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
1115
- compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
1116
- }
1117
- if (!options.experimental_isLazyBundle) for (const name of options.mainThreadChunks ?? []){
1118
- const asset = compilation.getAsset(name);
1119
- if (!asset) continue;
1120
- const source = asset.source.source().toString();
1121
- const useStrictPrefix = /^(['"])use strict\1;?/.test(source) ? "'use strict';" : '';
1122
- compilation.updateAsset(name, (old)=>new ConcatSource(`${useStrictPrefix}var globDynamicComponentEntry=globDynamicComponentEntry||'__Card__';`, old));
1123
- }
1124
- });
1125
- }
1126
- const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
1127
- const { ConcatSource } = compiler.webpack.sources;
1128
- hooks.beforeEncode.tap(this.constructor.name, (args)=>{
1129
- const { encodeData } = args;
1130
- encodeData.sourceContent.config = {
1131
- ...encodeData.sourceContent.config,
1132
- enableCSSInlineVariables: true
1133
- };
1134
- if (!encodeData.lepusCode.root) return args;
1135
- if ('card' === encodeData.sourceContent.appType) return args;
1136
- compilation.updateAsset(encodeData.lepusCode.root.name, (old)=>new ConcatSource(`\
1137
- (function (globDynamicComponentEntry) {
1138
- const module = { exports: {} }
1139
- const exports = module.exports
1140
- `, old, `
1141
- ;return module.exports
1142
- })`));
1143
- return args;
1866
+ const transformModule = async (context, background)=>{
1867
+ const hot = hotUpdatesEnabled(context.environment);
1868
+ const isJs = /\.[cm]?js$/.test(context.resourcePath);
1869
+ if (isJs) {
1870
+ const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
1871
+ skipLinker: false,
1872
+ sideEffects: ()=>Promise.resolve(false)
1873
+ });
1874
+ const code = transformCrossThread(transformWorklets(Buffer.from(contents).toString(), context.resourcePath), context.resourcePath, {
1875
+ root: api.context.rootPath
1144
1876
  });
1877
+ if (!background) return {
1878
+ code
1879
+ };
1880
+ const wrapped = wrapPureClasses(code, context.resourcePath);
1881
+ return {
1882
+ code: hot && backgroundEntries.isEntryFile(context.resourcePath) ? addEntryHotAccept(wrapped) : wrapped
1883
+ };
1884
+ }
1885
+ const content = typeScriptFileCache.get(context.resourcePath);
1886
+ if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
1887
+ for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
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
1145
1897
  });
1898
+ if (!background) return {
1899
+ code
1900
+ };
1901
+ return {
1902
+ code: wrapPureClasses(Buffer.from(await javascriptTransformer.transformData(context.resourcePath, code, {
1903
+ skipLinker: true,
1904
+ sideEffects: ()=>Promise.resolve(false)
1905
+ })).toString(), context.resourcePath)
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
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;
1146
2021
  }
1147
- #updateMainThreadInfo(compilation, name) {
1148
- const asset = compilation.getAsset(name);
1149
- tiny_invariant(asset, `Should have main thread asset ${name}`);
1150
- compilation.updateAsset(asset.name, asset.source, {
1151
- ...asset.info,
1152
- 'lynx:main-thread': true
1153
- });
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;
1154
2033
  }
1155
- }
1156
- const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
1157
- const PLUGIN_NAME_TEMPLATE = 'lynx:template';
1158
- const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
1159
- const PLUGIN_NAME_ANGULAR = 'lynx:angular';
1160
- const applyEntry = (api, options)=>{
1161
- const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
1162
- api.modifyBundlerChain((chain, { environment, isDev })=>{
1163
- const lynxConfig = api.useExposed(S_LYNX_CONFIG);
1164
- if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
1165
- const isLynx = 'lynx' === environment.name;
1166
- const isWeb = 'web' === environment.name;
1167
- const { hmr, liveReload } = environment.config.dev ?? {};
1168
- const enabledHMR = isDev && !isWeb && false !== hmr;
1169
- const enabledLiveReload = isDev && !isWeb && false !== liveReload;
1170
- const entries = chain.entryPoints.entries() ?? {};
1171
- chain.entryPoints.clear();
1172
- const mainThreadChunks = [];
1173
- for (const [entryName, entryPoint] of Object.entries(entries)){
1174
- const { imports } = getChunks(entryName, entryPoint.values());
1175
- const templateFilename = lynxConfig.resolveBundleFilename({
1176
- entryName,
1177
- platform: environment.name
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));
2039
+ const applyCSS = (api, options)=>{
2040
+ const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
2041
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
2042
+ output: {
2043
+ injectStyles: false
2044
+ }
2045
+ }));
2046
+ const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
2047
+ api.modifyBundlerChain(async (chain, { CHAIN_ID, environment })=>{
2048
+ const { CssExtractRspackPlugin } = await import("@lynx-js/css-extract-webpack-plugin");
2049
+ const removeLightningCSS = (rule)=>{
2050
+ if (rule.uses.has(CHAIN_ID.USE.LIGHTNINGCSS) && 'lynx' === environment.name) rule.uses.delete(CHAIN_ID.USE.LIGHTNINGCSS);
2051
+ };
2052
+ const cssRules = [
2053
+ CHAIN_ID.RULE.CSS,
2054
+ CHAIN_ID.RULE.SASS,
2055
+ CHAIN_ID.RULE.LESS,
2056
+ CHAIN_ID.RULE.STYLUS
2057
+ ];
2058
+ for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
2059
+ const rule = chain.module.rule(ruleName);
2060
+ const mainRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_MAIN : ruleName;
2061
+ const mainRule = rule.oneOf(mainRuleName);
2062
+ const parentRuleEntries = rule.entries();
2063
+ removeLightningCSS(mainRule);
2064
+ mainRule.issuerLayer(LAYERS.MAIN_THREAD).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
2065
+ const uses = mainRule.uses.entries() ?? {};
2066
+ const ruleEntries = mainRule.entries();
2067
+ const cssLoader = uses[CHAIN_ID.USE.CSS];
2068
+ if (!cssLoader) continue;
2069
+ const cssLoaderRule = cssLoader.entries();
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({
2072
+ dependency: parentRuleEntries.dependency
1178
2073
  });
1179
- const mainThreadEntry = `${entryName}__main-thread`;
1180
- const mainThreadName = node_path.posix.join(`${entryName}/main-thread.js`);
1181
- const backgroundEntry = entryName;
1182
- const backgroundThreadName = node_path.posix.join(`${entryName}/background-thread.js`);
1183
- mainThreadChunks.push(mainThreadName);
1184
- chain.entry(mainThreadEntry).add({
1185
- layer: LAYERS.MAIN_THREAD,
1186
- import: imports,
1187
- filename: mainThreadName
1188
- }).end();
1189
- chain.entry(backgroundEntry).add({
1190
- layer: LAYERS.BACKGROUND,
1191
- import: imports,
1192
- filename: backgroundThreadName
1193
- }).when(enabledHMR, (entry)=>{
1194
- entry.prepend({
1195
- layer: LAYERS.BACKGROUND,
1196
- import: '@rspack/core/hot/dev-server'
1197
- });
1198
- }).when(enabledHMR || enabledLiveReload, (entry)=>{
1199
- entry.prepend({
1200
- layer: LAYERS.BACKGROUND,
1201
- import: '@lynx-js/webpack-dev-transport/client'
1202
- });
1203
- }).end();
1204
- chain.plugin(`${PLUGIN_NAME_TEMPLATE}-${entryName}`).use(LynxTemplatePlugin, [
1205
- {
1206
- filename: templateFilename,
1207
- intermediate: lynxConfig.resolveIntermediateDir({
1208
- entryName
1209
- }),
1210
- chunks: [
1211
- mainThreadEntry,
1212
- backgroundEntry
1213
- ],
1214
- cssPlugins: [
1215
- CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
1216
- ],
1217
- customCSSInheritanceList,
1218
- debugInfoOutside,
1219
- defaultDisplayLinear,
1220
- enableAccessibilityElement,
1221
- enableCSSInheritance,
1222
- enableCSSInvalidation,
1223
- enableCSSSelector,
1224
- enableNewGesture,
1225
- enableRemoveCSSScope,
1226
- removeDescendantSelectorScope,
1227
- targetSdkVersion,
1228
- enableA11y: true
1229
- }
1230
- ]).end();
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();
1231
2075
  }
1232
- if (isLynx) {
1233
- chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
2076
+ for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
2077
+ const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
2078
+ removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
2079
+ }
2080
+ if (chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
2081
+ const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
2082
+ fontRule.oneOfs.clear();
2083
+ fontRule.type('asset/inline');
2084
+ }
2085
+ chain.plugin(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT).tap(([options])=>[
1234
2086
  {
1235
- injectVars (vars) {
1236
- return vars.map((name)=>{
1237
- if ('Component' === name) return '__Component';
1238
- return name;
1239
- });
1240
- },
2087
+ ...options,
2088
+ enableRemoveCSSScope,
2089
+ enableCSSSelector,
2090
+ enableCSSInvalidation,
1241
2091
  targetSdkVersion,
1242
- test: /^(?!.*main-thread(?:\.[A-Fa-f0-9]*)?\.js$).*\.js$/
2092
+ cssPlugins: [
2093
+ CSSPlugins.parserPlugins.removeFunctionWhiteSpace()
2094
+ ]
1243
2095
  }
1244
- ]).end();
1245
- chain.plugin(`${LynxEncodePlugin.name}`).use(LynxEncodePlugin, [
1246
- {}
1247
- ]).end();
1248
- }
1249
- if (isWeb) chain.plugin(`${WebEncodePlugin.name}`).use(WebEncodePlugin, []).end();
1250
- chain.plugin(PLUGIN_NAME_ANGULAR).after(PLUGIN_NAME_TEMPLATE).use(AngularWebpackPlugin, [
1251
- {
1252
- mainThreadChunks,
1253
- enableSSR: options.enableSSR,
1254
- isWeb
1255
- }
1256
- ]);
2096
+ ]).init((_, args)=>new CssExtractRspackPlugin(...args)).end().end();
2097
+ chain.module.when(void 0 === enableRemoveCSSScope, (module)=>module.rule('lynx.css.scoped').test(/\.css$/).resourceQuery({
2098
+ and: [
2099
+ /cssId/,
2100
+ {
2101
+ not: /common/
2102
+ }
2103
+ ]
2104
+ }).sideEffects(false));
1257
2105
  });
1258
2106
  };
1259
- const getChunks = (entryName, entryValue)=>{
1260
- const chunks = [
1261
- entryName
1262
- ];
1263
- const imports = [];
1264
- for (const item of entryValue){
1265
- if ('string' == typeof item) {
1266
- imports.push(item);
1267
- continue;
1268
- }
1269
- if (Array.isArray(item)) {
1270
- imports.push(...imports);
1271
- continue;
1272
- }
1273
- const { dependOn } = item;
1274
- if (Array.isArray(item.import)) imports.push(...item.import);
1275
- else imports.push(item.import);
1276
- if (dependOn) if ('string' == typeof dependOn) chunks.unshift(dependOn);
1277
- else chunks.unshift(...dependOn);
2107
+ const normalizeCssLoaderOptions = (options, exportOnlyLocals)=>{
2108
+ if (options.modules && exportOnlyLocals) {
2109
+ let { modules } = options;
2110
+ modules = true === modules ? {
2111
+ exportOnlyLocals: true
2112
+ } : 'string' == typeof modules ? {
2113
+ mode: modules,
2114
+ exportOnlyLocals: true
2115
+ } : {
2116
+ ...modules,
2117
+ exportOnlyLocals: true
2118
+ };
2119
+ return {
2120
+ ...options,
2121
+ modules
2122
+ };
1278
2123
  }
1279
- return {
1280
- chunks,
1281
- imports
1282
- };
2124
+ return options;
1283
2125
  };
1284
2126
  const applyGenerator = (api)=>{
1285
2127
  api.modifyBundlerChain({
@@ -1291,6 +2133,21 @@ const applyGenerator = (api)=>{
1291
2133
  }
1292
2134
  });
1293
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';
1294
2151
  const RESET = '\x1b[0m';
1295
2152
  const GRAY = '\x1b[90m';
1296
2153
  const RED = '\x1b[31m';
@@ -1325,7 +2182,9 @@ const formatArg = (arg)=>{
1325
2182
  if ('string' == typeof arg) return arg;
1326
2183
  if ('object' == typeof arg && 'Error' === arg.__type) {
1327
2184
  const err = arg;
1328
- return err.stack || `${err.name}: ${err.message}`;
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}`;
1329
2188
  }
1330
2189
  try {
1331
2190
  return JSON.stringify(arg, null, 2);
@@ -1333,28 +2192,19 @@ const formatArg = (arg)=>{
1333
2192
  return String(arg);
1334
2193
  }
1335
2194
  };
1336
- let logFile = null;
1337
- let logFilePath = '';
1338
- const getLogFilePath = ()=>logFilePath;
1339
- const ensureLogFile = ()=>{
1340
- if (!logFile) {
1341
- logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
1342
- logFile = node_fs.createWriteStream(logFilePath, {
1343
- flags: 'w'
1344
- });
1345
- }
1346
- return logFile;
1347
- };
1348
- const writeLog = (entry)=>{
1349
- const stream = ensureLogFile();
2195
+ const writeLog = (stream, entry)=>{
1350
2196
  const color = LEVEL_COLORS[entry.level] || WHITE;
1351
2197
  const label = LEVEL_LABELS[entry.level] || entry.level.toUpperCase();
1352
2198
  const time = formatTimestamp(entry.timestamp);
1353
2199
  const args = entry.args.map(formatArg).join(' ');
1354
2200
  stream.write(`${GRAY}${time}${RESET} ${color}${label}${RESET} ${args}\n`);
1355
2201
  };
1356
- const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1357
- if ('OPTIONS' === req.method && '/__dev_logs' === req.url) {
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) {
1358
2208
  res.writeHead(204, {
1359
2209
  'Access-Control-Allow-Origin': '*',
1360
2210
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
@@ -1363,7 +2213,7 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1363
2213
  res.end();
1364
2214
  return;
1365
2215
  }
1366
- if ('POST' !== req.method || '/__dev_logs' !== req.url) return void next();
2216
+ if ('POST' !== req.method || req.url !== DEV_LOGS_PATH) return void next();
1367
2217
  let body = '';
1368
2218
  req.on('data', (chunk)=>{
1369
2219
  body += chunk.toString();
@@ -1375,22 +2225,21 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1375
2225
  res.end();
1376
2226
  try {
1377
2227
  const { logs } = JSON.parse(body);
1378
- for (const entry of logs)writeLog(entry);
2228
+ for (const entry of logs)writeLog(logFile, entry);
1379
2229
  } catch {}
1380
2230
  });
1381
2231
  };
1382
- const LOG_PORT_OFFSET = 1;
2232
+ };
2233
+ const FALLBACK_ORIGIN = 'http://localhost:3000';
1383
2234
  const applyDevLogger = (api)=>{
1384
2235
  api.modifyRspackConfig((rspackConfig)=>{
1385
2236
  if ('development' !== rspackConfig.mode) return rspackConfig;
1386
2237
  const publicPath = rspackConfig.output?.publicPath;
1387
- let logUrl = '';
2238
+ let origin = FALLBACK_ORIGIN;
1388
2239
  if ('string' == typeof publicPath && publicPath.startsWith('http')) try {
1389
- const parsed = new URL(publicPath);
1390
- const logPort = Number(parsed.port || 80) + LOG_PORT_OFFSET;
1391
- logUrl = `${parsed.protocol}//${parsed.hostname}:${logPort}/__dev_logs`;
2240
+ origin = new URL(publicPath).origin;
1392
2241
  } catch {}
1393
- if (!logUrl) logUrl = `http://localhost:${3000 + LOG_PORT_OFFSET}/__dev_logs`;
2242
+ const logUrl = `${origin}${DEV_LOGS_PATH}`;
1394
2243
  rspackConfig.plugins = rspackConfig.plugins || [];
1395
2244
  rspackConfig.plugins.push({
1396
2245
  name: 'lynx:angular:dev-logger-define',
@@ -1403,22 +2252,12 @@ const applyDevLogger = (api)=>{
1403
2252
  });
1404
2253
  return rspackConfig;
1405
2254
  });
1406
- api.onAfterStartDevServer(({ port })=>{
1407
- const logPort = port + LOG_PORT_OFFSET;
1408
- const middleware = createDevLoggerMiddleware();
1409
- const server = node_http.createServer((req, res)=>{
1410
- middleware(req, res, ()=>{
1411
- res.writeHead(404);
1412
- res.end();
1413
- });
1414
- });
1415
- server.listen(logPort, '0.0.0.0', ()=>{
1416
- console.log(`[dev-logger] Log server on :${logPort} → tail -f ${getLogFilePath()}`);
1417
- });
1418
- server.on('error', (err)=>{
1419
- if ('EADDRINUSE' === err.code) server.listen(logPort + 1, '0.0.0.0');
1420
- else console.warn('[dev-logger] Failed to start log server:', err.message);
1421
- });
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}`);
1422
2261
  });
1423
2262
  };
1424
2263
  const applySplitChunksRule = (api, options)=>{
@@ -1432,8 +2271,7 @@ const applySplitChunksRule = (api, options)=>{
1432
2271
  });
1433
2272
  return config;
1434
2273
  });
1435
- api.modifyRspackConfig((rspackConfig, { environment })=>{
1436
- if ('lynx' !== environment.name) return rspackConfig;
2274
+ api.modifyRspackConfig((rspackConfig)=>{
1437
2275
  rspackConfig.output = rspackConfig.output ?? {};
1438
2276
  if (!options.experimental_isLazyBundle) rspackConfig.output.asyncChunks = false;
1439
2277
  if (!rspackConfig.optimization) return rspackConfig;
@@ -1455,16 +2293,28 @@ const TAILWIND_CONFIG_FILES = [
1455
2293
  'tailwind.config.mjs',
1456
2294
  'tailwind.config.cjs'
1457
2295
  ];
2296
+ const isTailwind = (plugin)=>'function' == typeof plugin ? 'tailwindcss' === plugin.name : plugin?.postcssPlugin === 'tailwindcss';
1458
2297
  const applyTailwind = (api)=>{
1459
2298
  const configFile = findUp(TAILWIND_CONFIG_FILES, process.cwd());
1460
2299
  if (!configFile) return;
1461
- pluginTailwindCSS({
1462
- config: configFile,
1463
- exclude: [
1464
- /[\\/]node_modules[\\/]/
1465
- ]
1466
- }).setup(api);
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
+ }));
1467
2311
  };
2312
+ const DEPRECATED_OPTIONS = [
2313
+ 'enableICU',
2314
+ 'enableParallelElement',
2315
+ 'pipelineSchedulerConfig'
2316
+ ];
2317
+ const deprecatedOptionsIn = (options)=>DEPRECATED_OPTIONS.filter((name)=>options?.[name] !== void 0);
1468
2318
  const normalizeOptions = (options)=>{
1469
2319
  const defaultOptions = {
1470
2320
  customCSSInheritanceList: void 0,
@@ -1474,37 +2324,43 @@ const normalizeOptions = (options)=>{
1474
2324
  enableCSSInheritance: false,
1475
2325
  enableCSSInvalidation: false,
1476
2326
  enableCSSSelector: true,
1477
- enableICU: false,
1478
2327
  enableNewGesture: true,
1479
- enableParallelElement: true,
1480
2328
  defaultDisplayLinear: true,
1481
2329
  enableRemoveCSSScope: false,
1482
2330
  enableSSR: false,
1483
2331
  pages: void 0,
1484
- pipelineSchedulerConfig: 0x00010000,
1485
2332
  targetSdkVersion: '3.2',
1486
2333
  defaultOverflowVisible: true,
1487
2334
  removeDescendantSelectorScope: false,
1488
2335
  experimental_isLazyBundle: false
1489
2336
  };
1490
- return Object.assign(defaultOptions, options);
2337
+ const normalized = Object.assign(defaultOptions, options);
2338
+ for (const name of DEPRECATED_OPTIONS)delete normalized[name];
2339
+ return normalized;
1491
2340
  };
1492
2341
  const pluginAngularLynx = (options)=>({
1493
2342
  name: 'lynx:angular',
1494
2343
  pre: [
1495
- 'lynx:rsbuild:plugin-api'
2344
+ 'lynx:rsbuild:plugin-api',
2345
+ ANGULAR_CLI_CONTEXT_PLUGIN_NAME
1496
2346
  ],
1497
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.`);
1498
2349
  const normalizedOptions = normalizeOptions(options);
2350
+ const backgroundEntries = createBackgroundEntries({
2351
+ rootPath: api.context.rootPath
2352
+ });
1499
2353
  applyCSS(api, normalizedOptions);
1500
2354
  applyTailwind(api);
1501
- applyEntry(api, normalizedOptions);
2355
+ const appEntries = new Set();
2356
+ applyEntry(api, normalizedOptions, backgroundEntries, appEntries);
1502
2357
  applyLayers(api);
1503
- const angularRules = applyAngularRules(api, normalizedOptions);
2358
+ applyResolve(api);
2359
+ const angularRules = applyAngularRules(api, normalizedOptions, backgroundEntries, appEntries);
1504
2360
  applyGenerator(api);
1505
2361
  applySplitChunksRule(api, normalizedOptions);
1506
2362
  applyDevLogger(api);
1507
2363
  await angularRules;
1508
2364
  }
1509
2365
  });
1510
- export { pluginAngularLynx };
2366
+ export { pluginAngularCliContext, pluginAngularLynx };