@blotch/rsbuild-plugin-angular-lynx 22.2.2 → 22.2.4

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 +2 -1
  2. package/dist/544.js +21 -0
  3. package/dist/angular-rebuild-coordinator.d.ts +1 -1
  4. package/dist/angular.d.ts +3 -2
  5. package/dist/background-entry.d.ts +96 -0
  6. package/dist/client/background-hot-updates.d.ts +50 -0
  7. package/dist/client/background-hot-updates.js +77 -0
  8. package/dist/client/background-hot-updates.test.d.ts +1 -0
  9. package/dist/client/css-hot-update.cjs +9 -0
  10. package/dist/client/css-hot-update.d.ts +44 -0
  11. package/dist/client/css-hot-update.js +42 -0
  12. package/dist/client/css-hot-update.test.d.ts +1 -0
  13. package/dist/client/hmr-background.js +16 -0
  14. package/dist/client/hmr-main-thread.js +9 -0
  15. package/dist/client/hmr-protocol.d.ts +74 -0
  16. package/dist/client/main-thread-hot-updates.d.ts +40 -0
  17. package/dist/client/main-thread-hot-updates.js +78 -0
  18. package/dist/client/main-thread-hot-updates.test.d.ts +1 -0
  19. package/dist/client/testing/test-context.d.ts +16 -0
  20. package/dist/component-styles-cache.d.ts +5 -35
  21. package/dist/component-styles.d.ts +73 -0
  22. package/dist/component-styles.test.d.ts +1 -0
  23. package/dist/cross-thread-transform.d.ts +38 -0
  24. package/dist/cross-thread-transform.test.d.ts +1 -0
  25. package/dist/css.d.ts +2 -2
  26. package/dist/entry.d.ts +23 -2
  27. package/dist/hot-updates.d.ts +67 -0
  28. package/dist/hot-updates.test.d.ts +1 -0
  29. package/dist/index.js +2559 -173
  30. package/dist/layers.d.ts +6 -4
  31. package/dist/logger/dev-logger-middleware.d.ts +13 -7
  32. package/dist/logger/dev-logger.d.ts +13 -0
  33. package/dist/polyfills.js +56 -0
  34. package/dist/polyfills.test.d.ts +1 -0
  35. package/dist/pure-classes.d.ts +27 -0
  36. package/dist/pure-classes.test.d.ts +1 -0
  37. package/dist/resolve.d.ts +19 -0
  38. package/dist/resolve.test.d.ts +1 -0
  39. package/dist/split-chunks.d.ts +2 -2
  40. package/dist/styles/blur-view.css +16 -0
  41. package/dist/tailwind-v4/calc.d.ts +18 -0
  42. package/dist/tailwind-v4/calc.test.d.ts +1 -0
  43. package/dist/tailwind-v4/colors.d.ts +6 -0
  44. package/dist/tailwind-v4/colors.test.d.ts +1 -0
  45. package/dist/tailwind-v4/declarations.d.ts +39 -0
  46. package/dist/tailwind-v4/declarations.test.d.ts +1 -0
  47. package/dist/tailwind-v4/index.d.ts +23 -0
  48. package/dist/tailwind-v4/index.test.d.ts +1 -0
  49. package/dist/tailwind-v4/lynx-properties.d.ts +20 -0
  50. package/dist/tailwind-v4/selectors.d.ts +6 -0
  51. package/dist/tailwind-v4/selectors.test.d.ts +1 -0
  52. package/dist/tailwind-v4/structure.d.ts +51 -0
  53. package/dist/tailwind-v4/structure.test.d.ts +1 -0
  54. package/dist/tailwind-v4/variables.d.ts +20 -0
  55. package/dist/tailwind-v4/variables.test.d.ts +1 -0
  56. package/dist/tailwind.d.ts +14 -0
  57. package/dist/transform-module.d.ts +43 -17
  58. package/dist/utils/angular/component-scope-id.d.ts +5 -3
  59. package/dist/utils/options.d.ts +40 -1
  60. package/package.json +5 -2
  61. package/dist/loaders/thread-globals-loader.d.ts +0 -8
  62. package/dist/loaders/thread-globals-loader.js +0 -6
  63. /package/dist/{loaders/thread-globals-loader.test.d.ts → background-entry.test.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,19 +1,18 @@
1
1
  /*! LICENSE: index.js.LICENSE.txt */
2
2
  import node_fs, { existsSync } from "node:fs";
3
- import node_path, { join, resolve } from "node:path";
3
+ import node_path, { join, resolve as external_node_path_resolve } from "node:path";
4
4
  import { CSSPlugins, LynxEncodePlugin, LynxTemplatePlugin, WebEncodePlugin } from "@lynx-js/template-webpack-plugin";
5
+ import { createRequire } from "node:module";
6
+ import { fileURLToPath } from "node:url";
5
7
  import { RuntimeWrapperWebpackPlugin } from "@lynx-js/runtime-wrapper-webpack-plugin";
6
8
  import { RuntimeGlobals } from "@lynx-js/webpack-runtime-globals";
7
9
  import tiny_invariant from "tiny-invariant";
8
10
  import { createHash } from "node:crypto";
9
- import { fileURLToPath } from "node:url";
10
- import { createRequire } from "node:module";
11
11
  import { availableParallelism } from "node:os";
12
12
  import { workspaces } from "@angular-devkit/core";
13
13
  import { NodeJsSyncHost } from "@angular-devkit/core/node";
14
14
  import { access, constants } from "node:fs/promises";
15
- import node_http from "node:http";
16
- import { pluginTailwindCSS } from "rsbuild-plugin-tailwindcss";
15
+ import postcss_value_parser from "postcss-value-parser";
17
16
  import * as __rspack_external_typescript from "typescript";
18
17
  import * as __rspack_external_node_path_c5b9b54f from "node:path";
19
18
  const ANGULAR_CLI_CONTEXT = Symbol.for('@blotch/rsbuild-plugin-angular-lynx:angular-cli-context');
@@ -90,19 +89,32 @@ const LAYERS = {
90
89
  };
91
90
  const applyLayers = (api)=>{
92
91
  api.modifyBundlerChain((chain)=>{
93
- chain.module.rule("typescript").oneOf(LAYERS.BACKGROUND).layer(LAYERS.BACKGROUND).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
92
+ for (const [layer, target, isMainThread] of [
93
+ [
94
+ LAYERS.BACKGROUND,
95
+ 'es2015',
96
+ false
97
+ ],
98
+ [
99
+ LAYERS.MAIN_THREAD,
100
+ 'es2019',
101
+ true
102
+ ]
103
+ ])chain.module.rule("typescript").oneOf(layer).issuerLayer(layer).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
94
104
  jsc: {
95
- target: 'es2015',
96
- parser: {
97
- syntax: "typescript"
98
- }
99
- }
100
- }).end();
101
- chain.module.rule("typescript").oneOf(LAYERS.MAIN_THREAD).layer(LAYERS.MAIN_THREAD).test(/\.[cm]?[jt]sx?$/).use('builtin:swc-loader').loader('builtin:swc-loader').options({
102
- jsc: {
103
- target: 'es2019',
105
+ target,
104
106
  parser: {
105
107
  syntax: "typescript"
108
+ },
109
+ transform: {
110
+ optimizer: {
111
+ simplify: false,
112
+ globals: {
113
+ vars: {
114
+ __MAIN_THREAD__: String(isMainThread)
115
+ }
116
+ }
117
+ }
106
118
  }
107
119
  }
108
120
  }).end();
@@ -201,8 +213,9 @@ class AngularWebpackPlugin {
201
213
  name: `${this.constructor.name}:threadGlobals`,
202
214
  stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
203
215
  }, ()=>{
204
- for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js'))){
205
- const isMainThread = mainThreadChunkSet.has(name);
216
+ this.#markMainThreadHotUpdates(compilation, mainThreadChunkSet);
217
+ for (const [name] of Object.entries(compilation.assets).filter(([n])=>n.endsWith('.js') && !compilation.getAsset(n)?.info.hotModuleReplacement)){
218
+ const isMainThread = mainThreadChunkSet.has(name) || compilation.getAsset(name)?.info['lynx:main-thread'] === true;
206
219
  const prefix = `globalThis["__MAIN_THREAD__"]=${isMainThread};`;
207
220
  compilation.updateAsset(name, (old)=>new ConcatSource(prefix, old));
208
221
  }
@@ -236,6 +249,16 @@ class AngularWebpackPlugin {
236
249
  });
237
250
  });
238
251
  }
252
+ #markMainThreadHotUpdates(compilation, mainThreadChunkSet) {
253
+ for (const chunk of compilation.chunks){
254
+ const files = [
255
+ ...chunk.files
256
+ ];
257
+ if (files.some((file)=>mainThreadChunkSet.has(file) || !compilation.getAsset(file)?.info.hotModuleReplacement && compilation.getAsset(file)?.info['lynx:main-thread'])) {
258
+ for (const file of files)if (file.endsWith('.js') && compilation.getAsset(file)?.info.hotModuleReplacement) this.#updateMainThreadInfo(compilation, file);
259
+ }
260
+ }
261
+ }
239
262
  #updateMainThreadInfo(compilation, name) {
240
263
  const asset = compilation.getAsset(name);
241
264
  tiny_invariant(asset, `Should have main thread asset ${name}`);
@@ -249,9 +272,17 @@ const S_LYNX_CONFIG = Symbol.for('@lynx-js/rsbuild-plugin:config');
249
272
  const PLUGIN_NAME_TEMPLATE = 'lynx:template';
250
273
  const PLUGIN_NAME_RUNTIME_WRAPPER = 'lynx:runtime-wrapper';
251
274
  const PLUGIN_NAME_ANGULAR = 'lynx:angular';
252
- const applyEntry = (api, options)=>{
253
- const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, enableA11y, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
254
- api.modifyBundlerChain((chain, { environment, isDev })=>{
275
+ const RULE_BACKGROUND_MODULES = 'lynx:background-modules';
276
+ const PLUGIN_NAME_CSS_HOT_UPDATE = 'lynx:css-hot-update';
277
+ const BLUR_VIEW_STYLESHEET = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './styles/blur-view.css');
278
+ const HMR_BACKGROUND_CLIENT = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/hmr-background.js');
279
+ const HMR_MAIN_THREAD_CLIENT = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/hmr-main-thread.js');
280
+ const CSS_HOT_UPDATE = node_path.resolve(node_path.dirname(fileURLToPath(import.meta.url)), './client/css-hot-update.cjs');
281
+ const CSS_EXTRACT_HOT_UPDATE = /css-extract-webpack-plugin[\\/]runtime[\\/]hotModuleReplacement\.cjs$/;
282
+ const resolveReloadApp = ()=>createRequire(createRequire(import.meta.url).resolve('@lynx-js/rsbuild-plugin')).resolve('@lynx-js/webpack-dev-transport/reloadApp');
283
+ const applyEntry = (api, options, backgroundEntries, appEntries)=>{
284
+ const { customCSSInheritanceList, debugInfoOutside, defaultDisplayLinear, defaultOverflowVisible, enableA11y, enableAccessibilityElement, enableCSSInheritance, enableCSSInvalidation, enableCSSSelector, enableNewGesture, enableRemoveCSSScope, removeDescendantSelectorScope, targetSdkVersion } = options;
285
+ api.modifyBundlerChain((chain, { environment, isDev, rspack })=>{
255
286
  const lynxConfig = api.useExposed(S_LYNX_CONFIG);
256
287
  if (!lynxConfig) throw new Error('No Lynx config exposed. `pluginLynx` has to be applied for the Lynx build engine to be configured.');
257
288
  const isLynx = 'lynx' === environment.name;
@@ -261,9 +292,19 @@ const applyEntry = (api, options)=>{
261
292
  const enabledLiveReload = isDev && !isWeb && false !== liveReload;
262
293
  const entries = chain.entryPoints.entries() ?? {};
263
294
  chain.entryPoints.clear();
295
+ const preEntry = new Set(environment.config.source.preEntry);
264
296
  const mainThreadChunks = [];
265
297
  for (const [entryName, entryPoint] of Object.entries(entries)){
266
- const { imports } = getChunks(entryName, entryPoint.values());
298
+ const { imports: entryImports } = getChunks(entryName, entryPoint.values());
299
+ const mainThreadImports = isLynx ? [
300
+ BLUR_VIEW_STYLESHEET,
301
+ ...entryImports
302
+ ] : entryImports;
303
+ const backgroundImports = [
304
+ ...entryImports.filter((entryImport)=>preEntry.has(entryImport)),
305
+ backgroundEntries.fileFor(environment.name, entryName)
306
+ ];
307
+ for (const entryImport of entryImports)if (entryImport.startsWith('.') || node_path.isAbsolute(entryImport)) appEntries.add(node_path.resolve(api.context.rootPath, entryImport));
267
308
  const templateFilename = lynxConfig.resolveBundleFilename({
268
309
  entryName,
269
310
  platform: environment.name
@@ -278,17 +319,22 @@ const applyEntry = (api, options)=>{
278
319
  mainThreadChunks.push(mainThreadName);
279
320
  chain.entry(mainThreadEntry).add({
280
321
  layer: LAYERS.MAIN_THREAD,
281
- import: imports,
322
+ import: mainThreadImports,
282
323
  filename: mainThreadName
324
+ }).when(enabledHMR, (entry)=>{
325
+ entry.prepend({
326
+ layer: LAYERS.MAIN_THREAD,
327
+ import: HMR_MAIN_THREAD_CLIENT
328
+ });
283
329
  }).end();
284
330
  chain.entry(backgroundEntry).add({
285
331
  layer: LAYERS.BACKGROUND,
286
- import: imports,
332
+ import: backgroundImports,
287
333
  filename: backgroundThreadName
288
334
  }).when(enabledHMR, (entry)=>{
289
335
  entry.prepend({
290
336
  layer: LAYERS.BACKGROUND,
291
- import: '@rspack/core/hot/dev-server'
337
+ import: HMR_BACKGROUND_CLIENT
292
338
  });
293
339
  }).when(enabledHMR || enabledLiveReload, (entry)=>{
294
340
  entry.prepend({
@@ -315,6 +361,7 @@ const applyEntry = (api, options)=>{
315
361
  customCSSInheritanceList,
316
362
  debugInfoOutside,
317
363
  defaultDisplayLinear,
364
+ defaultOverflowVisible,
318
365
  enableAccessibilityElement,
319
366
  enableCSSInheritance,
320
367
  enableCSSInvalidation,
@@ -327,6 +374,14 @@ const applyEntry = (api, options)=>{
327
374
  }
328
375
  ]).end();
329
376
  }
377
+ chain.module.rule(RULE_BACKGROUND_MODULES).issuerLayer(LAYERS.BACKGROUND).test((file)=>backgroundEntries.isBackgroundModule(file)).sideEffects(true);
378
+ if (enabledHMR) {
379
+ chain.resolve.alias.set('@lynx-js/webpack-dev-transport/reloadApp', resolveReloadApp());
380
+ chain.plugin(PLUGIN_NAME_CSS_HOT_UPDATE).use(rspack.NormalModuleReplacementPlugin, [
381
+ CSS_EXTRACT_HOT_UPDATE,
382
+ CSS_HOT_UPDATE
383
+ ]).end();
384
+ }
330
385
  if (isLynx) {
331
386
  chain.plugin(PLUGIN_NAME_RUNTIME_WRAPPER).use(RuntimeWrapperWebpackPlugin, [
332
387
  {
@@ -473,7 +528,7 @@ const checkThresholds = (thresholds, size, label)=>thresholds.flatMap(({ limit,
473
528
  }
474
529
  ];
475
530
  });
476
- const sum = (sizes)=>sizes.reduce((total, { size })=>total + size, 0);
531
+ const budgets_sum = (sizes)=>sizes.reduce((total, { size })=>total + size, 0);
477
532
  const calculateSizes = (budget, assets, componentStyles)=>{
478
533
  switch(budget.type){
479
534
  case 'initial':
@@ -487,21 +542,21 @@ const calculateSizes = (budget, assets, componentStyles)=>{
487
542
  case 'bundle':
488
543
  return budget.name ? [
489
544
  {
490
- size: sum(assets.filter((asset)=>asset.entryName === budget.name)),
545
+ size: budgets_sum(assets.filter((asset)=>asset.entryName === budget.name)),
491
546
  label: budget.name
492
547
  }
493
548
  ] : [];
494
549
  case 'all':
495
550
  return [
496
551
  {
497
- size: sum(assets),
552
+ size: budgets_sum(assets),
498
553
  label: 'total'
499
554
  }
500
555
  ];
501
556
  case 'allScript':
502
557
  return [
503
558
  {
504
- size: sum(assets),
559
+ size: budgets_sum(assets),
505
560
  label: "total scripts"
506
561
  }
507
562
  ];
@@ -622,68 +677,111 @@ const buildLynxSchemaSourceFileCache = (tsconfig, shouldInclude = ()=>true)=>{
622
677
  fileNames
623
678
  };
624
679
  };
625
- const generateComponentScopeId = (className, containingFile)=>{
626
- const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
627
- return `l${hash}`;
628
- };
629
680
  const componentStyleSizeKey = (containingFile, stylesheetKey)=>`${containingFile}#${stylesheetKey}`;
630
681
  const forgetComponentStyleSizes = (componentStyleSizes, containingFile)=>{
631
682
  const prefix = componentStyleSizeKey(containingFile, '');
632
683
  for (const key of componentStyleSizes.keys())if (key.startsWith(prefix)) componentStyleSizes.delete(key);
633
684
  };
634
685
  const createTransformStylesheet = (params)=>{
635
- const { basePath, scopedCssCacheDir, componentStylesCache, componentScopeIds, onStylesheetWritten, componentStyleSizes } = params;
686
+ const { basePath, componentStyleSizes } = params;
636
687
  return async (data, containingFile, stylesheetFile, order, className)=>{
637
- const resolvedClassName = className ?? 'Component';
638
- const scopeId = generateComponentScopeId(resolvedClassName, containingFile);
639
- let componentStyles = componentStylesCache.get(containingFile);
640
- if (!componentStyles) {
641
- componentStyles = {
642
- imports: [],
643
- inlineStyles: [],
644
- scopeId,
645
- processedFiles: new Map()
646
- };
647
- componentStylesCache.set(containingFile, componentStyles);
648
- }
649
- const stylesheetKey = stylesheetFile ?? `__inline_${order}`;
650
- const previousPath = componentStyles.processedFiles.get(stylesheetKey);
651
- if (previousPath && !className) return '';
652
- componentStyleSizes?.set(componentStyleSizeKey(containingFile, stylesheetKey), {
688
+ const key = componentStyleSizeKey(containingFile, stylesheetFile ?? `__inline_${order}`);
689
+ if (componentStyleSizes && (className || !componentStyleSizes.has(key))) componentStyleSizes.set(key, {
653
690
  label: node_path.relative(basePath, stylesheetFile ?? containingFile).replace(/\\/g, '/'),
654
691
  size: Buffer.byteLength(data)
655
692
  });
656
- if (className || !componentScopeIds.has(containingFile)) componentScopeIds.set(containingFile, {
657
- className: resolvedClassName,
658
- scopeId
659
- });
660
- const scopedCss = data;
661
- const writeIfChanged = (filePath, content)=>{
662
- try {
663
- if (node_fs.readFileSync(filePath, 'utf-8') === content) return;
664
- } catch {}
665
- node_fs.writeFileSync(filePath, content);
666
- onStylesheetWritten?.(filePath);
667
- };
668
- let scopedPath;
669
- if (stylesheetFile) {
670
- const relName = node_path.relative(basePath, stylesheetFile);
671
- scopedPath = node_path.join(scopedCssCacheDir, `${relName.replace(/[/\\]/g, '__')}.__scoped_${scopeId}.css`);
672
- } else {
673
- const relDir = node_path.relative(basePath, node_path.dirname(containingFile));
674
- scopedPath = node_path.join(scopedCssCacheDir, `${relDir.replace(/[/\\]/g, '__')}__inline_${resolvedClassName}_${order}.__scoped_${scopeId}.css`);
693
+ return data;
694
+ };
695
+ };
696
+ const generateComponentScopeId = (className, containingFile)=>{
697
+ const hash = createHash('sha256').update(`${className}|${containingFile}`).digest('hex').slice(0, 10);
698
+ return `l${hash}`;
699
+ };
700
+ const EMULATED = 0;
701
+ const toLynxScopedCss = (css, scopeId)=>css.replaceAll('[_ngcontent-%COMP%]', `._ngcontent-${scopeId}`).replaceAll('[_nghost-%COMP%]', `._nghost-${scopeId}`).replaceAll('%COMP%', scopeId);
702
+ const fillCssVarNamespace = (css)=>css.replaceAll('%NS%', '');
703
+ const propertyName = (property)=>__rspack_external_typescript.isPropertyAssignment(property) && (__rspack_external_typescript.isIdentifier(property.name) || __rspack_external_typescript.isStringLiteral(property.name)) ? property.name.text : null;
704
+ const isDefineComponentCall = (node)=>{
705
+ if (!__rspack_external_typescript.isCallExpression(node)) return false;
706
+ const callee = node.expression;
707
+ const name = __rspack_external_typescript.isPropertyAccessExpression(callee) ? callee.name.text : __rspack_external_typescript.isIdentifier(callee) ? callee.text : null;
708
+ return 'ɵɵdefineComponent' === name;
709
+ };
710
+ const extractComponentStyles = (code, fileName)=>{
711
+ if (!code.includes('ɵɵdefineComponent')) return {
712
+ code,
713
+ components: []
714
+ };
715
+ const sourceFile = __rspack_external_typescript.createSourceFile(fileName, code, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.JS);
716
+ const components = [];
717
+ const removals = [];
718
+ const visit = (node)=>{
719
+ if (isDefineComponentCall(node)) {
720
+ const definition = node.arguments[0];
721
+ if (definition && __rspack_external_typescript.isObjectLiteralExpression(definition)) {
722
+ const properties = definition.properties;
723
+ const find = (name)=>properties.find((property)=>propertyName(property) === name);
724
+ const type = find('type')?.initializer;
725
+ const styles = find('styles');
726
+ const encapsulation = find('encapsulation')?.initializer;
727
+ 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)) {
728
+ const emulated = void 0 === encapsulation || __rspack_external_typescript.isNumericLiteral(encapsulation) && Number(encapsulation.text) === EMULATED;
729
+ const scopeId = emulated ? generateComponentScopeId(type.text, fileName) : void 0;
730
+ const stylesheets = styles.initializer.elements.map((element)=>{
731
+ const css = fillCssVarNamespace(element.text);
732
+ return scopeId ? toLynxScopedCss(css, scopeId) : css;
733
+ });
734
+ components.push({
735
+ className: type.text,
736
+ scopeId,
737
+ stylesheets
738
+ });
739
+ const index = properties.indexOf(styles);
740
+ const next = properties[index + 1];
741
+ removals.push({
742
+ start: styles.getStart(sourceFile),
743
+ end: next ? next.getStart(sourceFile) : properties.hasTrailingComma ? properties.end : styles.end
744
+ });
745
+ }
746
+ }
675
747
  }
676
- writeIfChanged(scopedPath, scopedCss);
677
- if (previousPath) {
678
- const idx = componentStyles.imports.indexOf(previousPath);
679
- if (idx >= 0) componentStyles.imports[idx] = scopedPath;
680
- else componentStyles.imports.push(scopedPath);
681
- if (previousPath !== scopedPath) try {
682
- node_fs.unlinkSync(previousPath);
748
+ __rspack_external_typescript.forEachChild(node, visit);
749
+ };
750
+ visit(sourceFile);
751
+ let result = code;
752
+ for (const { start, end } of removals.sort((a, b)=>b.start - a.start))result = result.slice(0, start) + result.slice(end);
753
+ return {
754
+ code: result,
755
+ components
756
+ };
757
+ };
758
+ const writeModuleStylesheets = (params)=>{
759
+ const { basePath, cacheDir, fileName, components, onWritten } = params;
760
+ if (0 === components.length) return;
761
+ const prefix = node_path.relative(basePath, fileName).replace(/[/\\]/g, '__').replace(/\.[cm]?[jt]s$/, '');
762
+ const imports = [];
763
+ const scopes = [];
764
+ for (const { className, scopeId, stylesheets } of components){
765
+ stylesheets.forEach((css, index)=>{
766
+ const filePath = node_path.join(cacheDir, `${prefix}.${className}.${index}.css`);
767
+ let current;
768
+ try {
769
+ current = node_fs.readFileSync(filePath, 'utf-8');
683
770
  } catch {}
684
- } else componentStyles.imports.push(scopedPath);
685
- componentStyles.processedFiles.set(stylesheetKey, scopedPath);
686
- return '';
771
+ if (current !== css) {
772
+ node_fs.writeFileSync(filePath, css);
773
+ onWritten?.(filePath);
774
+ }
775
+ imports.push(filePath);
776
+ });
777
+ if (scopeId) scopes.push({
778
+ className,
779
+ scopeId
780
+ });
781
+ }
782
+ return {
783
+ imports,
784
+ scopes
687
785
  };
688
786
  };
689
787
  const isLynxUnknownElementMessage = (text)=>(text?.includes('is not a known element') || text?.includes("isn't a known property of")) ?? false;
@@ -723,12 +821,267 @@ const toAngularDiagnosticError = (message, severity)=>{
723
821
  error.hideStack = true;
724
822
  return error;
725
823
  };
824
+ const PURE_CONSTRUCTORS = new Set([
825
+ 'Map',
826
+ 'Set',
827
+ 'WeakMap',
828
+ 'WeakSet'
829
+ ]);
830
+ /**
831
+ * Wraps each top-level class with private or static members in a pure IIFE,
832
+ * so a bundler can drop the classes nothing uses.
833
+ *
834
+ * The background layer compiles to ES2015 (layers.ts), so SWC lowers private
835
+ * and static class fields. A class declaration keeps its own statement, but the
836
+ * class expressions that bundled libraries are written in (`var X = class
837
+ * {…}`) become a sequence that assigns each private field's `WeakMap` to an
838
+ * outer variable: `var X = (_a = new WeakMap(), class {…})`. The assignment
839
+ * counts as a side effect, so rspack keeps every such class, and every import
840
+ * it uses, even when nothing uses the class. The runtime's own classes are
841
+ * like that (it uses `#private` fields throughout), which kept Angular's
842
+ * renderer and change detection in the background bundle.
843
+ *
844
+ * Angular's build already wraps a class whose static members are Angular's
845
+ * own (`ɵcmp`, `ɵfac` and so on) in `/*#__PURE__*\/ (() => {…})()`; this does
846
+ * the same for the rest. Inside the IIFE, the lowering's assignments are local
847
+ * and the call is marked pure. A class whose static members could have side
848
+ * effects of their own (anything but literals, functions and new collections)
849
+ * is left alone.
850
+ *
851
+ * @param code - The module's code, before SWC lowers it (and, for a `.ts`
852
+ * file, strips its types).
853
+ * @param filename - The module's path, for the parser.
854
+ * @returns The transformed code, or `code` itself when nothing applies.
855
+ */ const wrapPureClasses = (code, filename)=>{
856
+ if (!code.includes('class')) return code;
857
+ const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
858
+ const edits = [];
859
+ const wrap = (classNode)=>{
860
+ edits.push({
861
+ start: classNode.getStart(sourceFile),
862
+ end: classNode.getStart(sourceFile),
863
+ text: '/*#__PURE__*/ (() => '
864
+ }, {
865
+ start: classNode.end,
866
+ end: classNode.end,
867
+ text: ')()'
868
+ });
869
+ };
870
+ for (const statement of sourceFile.statements){
871
+ if (__rspack_external_typescript.isVariableStatement(statement)) {
872
+ const [declaration, ...rest] = statement.declarationList.declarations;
873
+ const value = declaration?.initializer;
874
+ if (0 === rest.length && value && __rspack_external_typescript.isClassExpression(value) && isLoweredPureClass(value)) wrap(value);
875
+ continue;
876
+ }
877
+ if (__rspack_external_typescript.isClassDeclaration(statement) && statement.name && isLoweredPureClass(statement)) {
878
+ const modifiers = __rspack_external_typescript.getModifiers(statement) ?? [];
879
+ const isExport = modifiers.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.ExportKeyword);
880
+ const isDefault = modifiers.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.DefaultKeyword);
881
+ const name = statement.name.text;
882
+ const start = statement.getStart(sourceFile);
883
+ const classStart = statement.getChildren(sourceFile).find((child)=>child.kind === __rspack_external_typescript.SyntaxKind.ClassKeyword).getStart(sourceFile);
884
+ // `class X {}` becomes `let X = /*#__PURE__*/ (() => class X {})();`.
885
+ edits.push({
886
+ start,
887
+ end: classStart,
888
+ text: `${isExport && !isDefault ? 'export ' : ''}let ${name} = /*#__PURE__*/ (() => `
889
+ }, {
890
+ start: statement.end,
891
+ end: statement.end,
892
+ text: `)();${isDefault ? `\nexport { ${name} as default };` : ''}`
893
+ });
894
+ }
895
+ }
896
+ if (0 === edits.length) return code;
897
+ edits.sort((a, b)=>b.start - a.start);
898
+ let result = code;
899
+ for (const { start, end, text } of edits)result = result.slice(0, start) + text + result.slice(end);
900
+ return result;
901
+ };
902
+ const isLoweredPureClass = (node)=>{
903
+ if (__rspack_external_typescript.getDecorators(node)?.length) return false;
904
+ const heritage = node.heritageClauses?.find((clause)=>clause.token === __rspack_external_typescript.SyntaxKind.ExtendsKeyword);
905
+ const base = heritage?.types[0]?.expression;
906
+ if (base && !isPureReference(base)) return false;
907
+ let lowered = false;
908
+ for (const member of node.members){
909
+ if (__rspack_external_typescript.isClassStaticBlockDeclaration(member)) return false;
910
+ const isStatic = __rspack_external_typescript.getModifiers(member)?.some((modifier)=>modifier.kind === __rspack_external_typescript.SyntaxKind.StaticKeyword);
911
+ const isPrivate = member.name && __rspack_external_typescript.isPrivateIdentifier(member.name);
912
+ if (member.name && __rspack_external_typescript.isComputedPropertyName(member.name)) return false;
913
+ if (isStatic || isPrivate) lowered = true;
914
+ if (isStatic && __rspack_external_typescript.isPropertyDeclaration(member) && member.initializer && !isPure(member.initializer)) return false;
915
+ }
916
+ return lowered;
917
+ };
918
+ const isPureReference = (node)=>__rspack_external_typescript.isIdentifier(node) || __rspack_external_typescript.isPropertyAccessExpression(node) && isPureReference(node.expression);
919
+ const isPure = (node)=>{
920
+ 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;
921
+ if (__rspack_external_typescript.isVoidExpression(node)) return isPure(node.expression);
922
+ if (__rspack_external_typescript.isParenthesizedExpression(node)) return isPure(node.expression);
923
+ if (__rspack_external_typescript.isPrefixUnaryExpression(node)) return isPure(node.operand);
924
+ if (__rspack_external_typescript.isArrayLiteralExpression(node)) return node.elements.every((element)=>!__rspack_external_typescript.isSpreadElement(element) && isPure(element));
925
+ 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));
926
+ return __rspack_external_typescript.isNewExpression(node) && __rspack_external_typescript.isIdentifier(node.expression) && PURE_CONSTRUCTORS.has(node.expression.text) && (node.arguments ?? []).every(isPure);
927
+ };
726
928
  const seedCompilationSourceFiles = (compilation, sourceFileCache)=>{
727
929
  const { sourceFiles } = compilation;
728
930
  if (!(sourceFiles instanceof Map)) return false;
729
931
  for (const [fileName, sourceFile] of sourceFileCache)sourceFiles.set(fileName, sourceFile);
730
932
  return true;
731
933
  };
934
+ const IN_PLACE_FIELDS = new Set([
935
+ 'template',
936
+ 'templateUrl',
937
+ 'styles',
938
+ 'styleUrl',
939
+ 'styleUrls'
940
+ ]);
941
+ const hotUpdatesEnabled = (environment)=>'web' !== environment.name && 'development' === environment.config.mode && environment.config.dev?.hmr !== false;
942
+ const hot_updates_hash = (text)=>createHash('sha1').update(text).digest('hex').slice(0, 16);
943
+ const isComponentDecorator = (decorator)=>{
944
+ const call = decorator.expression;
945
+ if (!__rspack_external_typescript.isCallExpression(call)) return false;
946
+ const callee = call.expression;
947
+ const name = __rspack_external_typescript.isPropertyAccessExpression(callee) ? callee.name.text : __rspack_external_typescript.isIdentifier(callee) ? callee.text : void 0;
948
+ return 'Component' === name;
949
+ };
950
+ const hotUpdateSignature = (source, fileName)=>{
951
+ const sourceFile = __rspack_external_typescript.createSourceFile(fileName, source, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.TS);
952
+ const removals = [];
953
+ const visit = (node)=>{
954
+ if (__rspack_external_typescript.isDecorator(node) && isComponentDecorator(node)) {
955
+ const metadata = node.expression.arguments[0];
956
+ if (metadata && __rspack_external_typescript.isObjectLiteralExpression(metadata)) {
957
+ 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({
958
+ start: property.initializer.getStart(sourceFile),
959
+ end: property.initializer.end
960
+ });
961
+ }
962
+ }
963
+ __rspack_external_typescript.forEachChild(node, visit);
964
+ };
965
+ visit(sourceFile);
966
+ let rest = source;
967
+ for (const { start, end } of removals.reverse())rest = rest.slice(0, start) + rest.slice(end);
968
+ return hot_updates_hash(rest);
969
+ };
970
+ const componentClassNames = (code, fileName)=>{
971
+ if (!code.includes('ɵɵdefineComponent')) return [];
972
+ const sourceFile = __rspack_external_typescript.createSourceFile(fileName, code, __rspack_external_typescript.ScriptTarget.Latest, true, __rspack_external_typescript.ScriptKind.JS);
973
+ const topLevelClasses = new Set(sourceFile.statements.filter(__rspack_external_typescript.isClassDeclaration).flatMap((statement)=>statement.name ? [
974
+ statement.name.text
975
+ ] : []));
976
+ const names = [];
977
+ const visit = (node)=>{
978
+ 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') {
979
+ const definition = node.arguments[0];
980
+ 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;
981
+ if (type && __rspack_external_typescript.isIdentifier(type) && topLevelClasses.has(type.text) && !names.includes(type.text)) names.push(type.text);
982
+ }
983
+ __rspack_external_typescript.forEachChild(node, visit);
984
+ };
985
+ visit(sourceFile);
986
+ return names;
987
+ };
988
+ const ACCEPT = 'ɵɵangularLynxHmrAccept';
989
+ const addHotAccept = (code, { components, signature, compiled })=>{
990
+ const list = `[${components.join(', ')}]`;
991
+ return `${code}
992
+ import { ɵhmrAccept as ${ACCEPT} } from '@blotch/angular-lynx';
993
+ if (import.meta.webpackHot) {
994
+ ${list} = ${ACCEPT}(import.meta.webpackHot, ${list}, ${JSON.stringify(signature)}, ${JSON.stringify(hot_updates_hash(compiled))});
995
+ }
996
+ `;
997
+ };
998
+ const addEntryHotAccept = (code)=>`${code}
999
+ if (import.meta.webpackHot) {
1000
+ import.meta.webpackHot.accept();
1001
+ }
1002
+ `;
1003
+ const RUNTIME_MODULE = '@blotch/angular-lynx';
1004
+ const TAGGED_CALLS = new Set([
1005
+ 'backgroundFn',
1006
+ 'createMainThreadRef'
1007
+ ]);
1008
+ const TAGGED_CLASSES = new Set([
1009
+ 'MainThreadRef'
1010
+ ]);
1011
+ const transformCrossThread = (code, filename, options)=>{
1012
+ const calls = findModuleLevelCalls(code, filename);
1013
+ if (0 === calls.length) return code;
1014
+ const fileKey = keyForFile(filename, options.root);
1015
+ const edits = [];
1016
+ calls.forEach((node, index)=>{
1017
+ const edit = tagEdit(node, JSON.stringify(`${fileKey}:${index}`), code);
1018
+ if (edit) edits.push(edit);
1019
+ });
1020
+ edits.sort((a, b)=>b.start - a.start);
1021
+ let result = code;
1022
+ for (const { start, end, text } of edits)result = result.slice(0, start) + text + result.slice(end);
1023
+ return result;
1024
+ };
1025
+ const definesCrossThreadValues = (code, filename)=>findModuleLevelCalls(code, filename).length > 0;
1026
+ const findModuleLevelCalls = (code, filename)=>{
1027
+ if (!code.includes(RUNTIME_MODULE) || !code.includes('backgroundFn') && !code.includes('MainThreadRef')) return [];
1028
+ const sourceFile = __rspack_external_typescript.createSourceFile(filename, code, __rspack_external_typescript.ScriptTarget.Latest, true);
1029
+ const runtime = runtimeBindings(sourceFile);
1030
+ const calls = [];
1031
+ const visit = (node, topLevel)=>{
1032
+ 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);
1033
+ const childTopLevel = topLevel && !__rspack_external_typescript.isFunctionLike(node) && !__rspack_external_typescript.isClassLike(node);
1034
+ __rspack_external_typescript.forEachChild(node, (child)=>visit(child, childTopLevel));
1035
+ };
1036
+ visit(sourceFile, true);
1037
+ return calls;
1038
+ };
1039
+ const tagEdit = (node, id, code)=>{
1040
+ const args = node.arguments ?? __rspack_external_typescript.factory.createNodeArray();
1041
+ if (args.length > 1 || args.some(__rspack_external_typescript.isSpreadElement)) return;
1042
+ if (1 === args.length) return {
1043
+ start: args[0].end,
1044
+ end: args[0].end,
1045
+ text: `, ${id}`
1046
+ };
1047
+ const close = node.getEnd() - 1;
1048
+ if (')' !== code[close]) return;
1049
+ return {
1050
+ start: close,
1051
+ end: close,
1052
+ text: `undefined, ${id}`
1053
+ };
1054
+ };
1055
+ const runtimeBindings = (sourceFile)=>{
1056
+ const named = new Map();
1057
+ const namespaces = new Set();
1058
+ for (const statement of sourceFile.statements){
1059
+ if (!__rspack_external_typescript.isImportDeclaration(statement) || !__rspack_external_typescript.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== RUNTIME_MODULE) continue;
1060
+ const bindings = statement.importClause?.namedBindings;
1061
+ if (bindings) {
1062
+ if (__rspack_external_typescript.isNamespaceImport(bindings)) {
1063
+ namespaces.add(bindings.name.text);
1064
+ continue;
1065
+ }
1066
+ for (const element of bindings.elements)named.set(element.name.text, (element.propertyName ?? element.name).text);
1067
+ }
1068
+ }
1069
+ return {
1070
+ named,
1071
+ namespaces
1072
+ };
1073
+ };
1074
+ const isRuntimeReference = (expression, runtime, exports)=>{
1075
+ if (__rspack_external_typescript.isIdentifier(expression)) {
1076
+ const imported = runtime.named.get(expression.text);
1077
+ return void 0 !== imported && exports.has(imported);
1078
+ }
1079
+ return __rspack_external_typescript.isPropertyAccessExpression(expression) && __rspack_external_typescript.isIdentifier(expression.expression) && runtime.namespaces.has(expression.expression.text) && exports.has(expression.name.text);
1080
+ };
1081
+ const keyForFile = (filename, root)=>{
1082
+ const relative = node_path.relative(root, filename).split(node_path.sep).join('/');
1083
+ return createHash('sha1').update(relative).digest('hex').slice(0, 10);
1084
+ };
732
1085
  const MAIN_THREAD_DIRECTIVE = 'main thread';
733
1086
  const MAIN_THREAD_DIRECTIVE_ALT = 'main-thread';
734
1087
  const transformWorklets = (code, filename)=>{
@@ -775,10 +1128,10 @@ const isAlreadyWrapped = (node)=>{
775
1128
  return false;
776
1129
  };
777
1130
  const buildTransformedCode = (params)=>{
778
- const { resourcePath, componentStyles, scopeInfo } = params;
1131
+ const { resourcePath, stylesheets, root, hot } = params;
779
1132
  let code = params.code;
780
- if (componentStyles) {
781
- const { imports } = componentStyles;
1133
+ if (stylesheets) {
1134
+ const { imports } = stylesheets;
782
1135
  let importsString = '';
783
1136
  for(let i = 0; i < imports.length; ++i){
784
1137
  let relativeImport = node_path.relative(node_path.dirname(resourcePath), imports[i]);
@@ -787,8 +1140,19 @@ const buildTransformedCode = (params)=>{
787
1140
  }
788
1141
  code = importsString + code;
789
1142
  }
790
- if (scopeInfo) code += `\n;${scopeInfo.className}.ɵcmp.id = '${scopeInfo.scopeId}';\n`;
791
- return transformWorklets(code, resourcePath);
1143
+ for (const { className, scopeId } of stylesheets?.scopes ?? [])code += `\n;${className}.ɵcmp.id = '${scopeId}';\n`;
1144
+ const transformed = transformCrossThread(transformWorklets(code, resourcePath), resourcePath, {
1145
+ root
1146
+ });
1147
+ if (!hot) return transformed;
1148
+ if (hot.entry) return addEntryHotAccept(transformed);
1149
+ const components = componentClassNames(params.code, resourcePath);
1150
+ if (0 === components.length) return transformed;
1151
+ return addHotAccept(transformed, {
1152
+ components,
1153
+ signature: hotUpdateSignature(hot.source(), resourcePath),
1154
+ compiled: params.code
1155
+ });
792
1156
  };
793
1157
  const applyAngularConfig = (api, buildOptions)=>{
794
1158
  const __dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -805,9 +1169,9 @@ const applyAngularConfig = (api, buildOptions)=>{
805
1169
  ];
806
1170
  else config.source.preEntry ??= [];
807
1171
  config.source.preEntry.push(node_path.resolve(__dirname, './polyfills'));
808
- if (buildOptions.i18n.hasDefinedSourceLocale) config.source.preEntry.push('@angular/localize/init');
809
- const polyfills = buildOptions.polyfills;
810
- if (polyfills) config.source.preEntry.push(...polyfills);
1172
+ const polyfills = buildOptions.polyfills ?? [];
1173
+ if (buildOptions.i18n.hasDefinedSourceLocale && !polyfills.some((polyfill)=>polyfill.startsWith('@angular/localize'))) config.source.preEntry.push('@angular/localize/init');
1174
+ config.source.preEntry.push(...polyfills);
811
1175
  config.source.preEntry.push(...buildOptions.styles);
812
1176
  config.source.tsconfigPath = buildOptions.tsconfig;
813
1177
  config.source.define ??= {};
@@ -916,7 +1280,7 @@ const normalizeCacheOptions = (projectMetadata, workspaceRoot)=>{
916
1280
  cacheEnabled = !isCI;
917
1281
  break;
918
1282
  }
919
- const cacheBasePath = resolve(workspaceRoot, path);
1283
+ const cacheBasePath = external_node_path_resolve(workspaceRoot, path);
920
1284
  return {
921
1285
  enabled: cacheEnabled,
922
1286
  basePath: cacheBasePath,
@@ -1325,7 +1689,7 @@ const reportLynxDiagnostics = (diagnostics)=>{
1325
1689
  for (const w of cssWarnings)console.warn(` ${w.message}`);
1326
1690
  }
1327
1691
  };
1328
- const applyAngularRules = async (api, pluginOptions)=>{
1692
+ const applyAngularRules = async (api, pluginOptions, backgroundEntries, appEntries)=>{
1329
1693
  const cli = getAngularCliContext(api);
1330
1694
  const { basePath, workspace } = await getAngularWorkspace(cli?.workspaceRoot);
1331
1695
  if (pluginOptions.pages) {
@@ -1365,25 +1729,22 @@ const applyAngularRules = async (api, pluginOptions)=>{
1365
1729
  const tsconfig = buildOptions.tsconfig;
1366
1730
  const compilation = await createAngularCompilation(false, aot);
1367
1731
  const typeScriptFileCache = new Map();
1368
- const scopedCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
1369
- node_fs.mkdirSync(scopedCssCacheDir, {
1732
+ const componentCssCacheDir = node_path.join(basePath, 'node_modules', '.cache', 'angular-lynx-css');
1733
+ node_fs.mkdirSync(componentCssCacheDir, {
1370
1734
  recursive: true
1371
1735
  });
1372
- const componentStylesCache = new Map();
1373
- const componentScopeIds = new Map();
1736
+ const moduleStylesheets = new Map();
1374
1737
  let componentResourceDependencies = new Map();
1375
1738
  const writtenStylesheets = new Set();
1376
1739
  let knownSourceFiles = new Set();
1740
+ const backgroundModules = new Set();
1741
+ let angularInputs = new Set();
1377
1742
  let angularDiagnostics = {
1378
1743
  errors: [],
1379
1744
  warnings: []
1380
1745
  };
1381
1746
  const transformStylesheet = createTransformStylesheet({
1382
1747
  basePath,
1383
- scopedCssCacheDir,
1384
- componentStylesCache,
1385
- componentScopeIds,
1386
- onStylesheetWritten: (filePath)=>writtenStylesheets.add(filePath),
1387
1748
  componentStyleSizes
1388
1749
  });
1389
1750
  const compileAngular = async (changedFiles)=>{
@@ -1420,8 +1781,22 @@ const applyAngularRules = async (api, pluginOptions)=>{
1420
1781
  try {
1421
1782
  for (const { filename, contents } of (await compilation.emitAffectedFiles())){
1422
1783
  const file = node_path.normalize(filename);
1423
- if (typeScriptFileCache.get(file) !== contents) changedOutputs.add(file);
1424
- typeScriptFileCache.set(file, contents);
1784
+ const { code, components } = extractComponentStyles('string' == typeof contents ? contents : Buffer.from(contents).toString(), file);
1785
+ const stylesheets = writeModuleStylesheets({
1786
+ basePath,
1787
+ cacheDir: componentCssCacheDir,
1788
+ fileName: file,
1789
+ components,
1790
+ onWritten: (filePath)=>writtenStylesheets.add(filePath)
1791
+ });
1792
+ if (stylesheets) moduleStylesheets.set(file, stylesheets);
1793
+ else moduleStylesheets.delete(file);
1794
+ if (typeScriptFileCache.get(file) !== code) {
1795
+ changedOutputs.add(file);
1796
+ if (definesCrossThreadValues(code, file)) backgroundModules.add(file);
1797
+ else backgroundModules.delete(file);
1798
+ }
1799
+ typeScriptFileCache.set(file, code);
1425
1800
  }
1426
1801
  } catch (error) {
1427
1802
  throw new Error(`Angular failed to emit compiled output for "${tsconfig}". Original error: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
@@ -1429,6 +1804,8 @@ const applyAngularRules = async (api, pluginOptions)=>{
1429
1804
  for (const file of writtenStylesheets)changedOutputs.add(file);
1430
1805
  for (const file of changedFiles ?? [])if (!node_fs.existsSync(file)) {
1431
1806
  typeScriptFileCache.delete(file);
1807
+ backgroundModules.delete(file);
1808
+ moduleStylesheets.delete(file);
1432
1809
  forgetComponentStyleSizes(componentStyleSizes, file);
1433
1810
  }
1434
1811
  reportLynxDiagnostics([
@@ -1438,9 +1815,16 @@ const applyAngularRules = async (api, pluginOptions)=>{
1438
1815
  ...scanSourcesForUnsupportedCss(fileNames)
1439
1816
  ]);
1440
1817
  angularDiagnostics = filterAngularDiagnostics(await compilation.diagnoseFiles(useTypeChecking ? DiagnosticModes.All : DiagnosticModes.All & ~DiagnosticModes.Semantic));
1818
+ const inputFiles = result.referencedFiles.map((file)=>node_path.normalize(file));
1819
+ angularInputs = new Set([
1820
+ ...inputFiles,
1821
+ ...[
1822
+ ...componentResourceDependencies.values()
1823
+ ].flat()
1824
+ ]);
1441
1825
  return {
1442
1826
  changedOutputs,
1443
- inputFiles: result.referencedFiles.map((file)=>node_path.normalize(file))
1827
+ inputFiles
1444
1828
  };
1445
1829
  };
1446
1830
  const coordinator = createAngularRebuildCoordinator({
@@ -1460,46 +1844,199 @@ const applyAngularRules = async (api, pluginOptions)=>{
1460
1844
  let compilationClosed = false;
1461
1845
  api.onBeforeEnvironmentCompile(async ({ environment, isWatch })=>{
1462
1846
  const compiler = compilersByEnvironment.get(environment.name);
1463
- const outdatedFiles = await coordinator.prepare(environment.name, [
1847
+ const changedFiles = [
1464
1848
  ...compiler?.modifiedFiles ?? [],
1465
1849
  ...compiler?.removedFiles ?? []
1466
- ]);
1467
- if (compiler && outdatedFiles.size > 0) compiler.modifiedFiles = new Set([
1850
+ ];
1851
+ const outdatedFiles = await coordinator.prepare(environment.name, changedFiles);
1852
+ const changedEntries = backgroundEntries.update(environment.name, {
1853
+ appModules: backgroundModules,
1854
+ watch: isWatch,
1855
+ rebuilt: outdatedFiles.size > 0 || changedFiles.some((file)=>!angularInputs.has(node_path.normalize(file)) && !backgroundEntries.isEntryFile(file))
1856
+ });
1857
+ if (compiler && outdatedFiles.size + changedEntries.length > 0) compiler.modifiedFiles = new Set([
1468
1858
  ...compiler.modifiedFiles ?? [],
1469
- ...outdatedFiles
1859
+ ...outdatedFiles,
1860
+ ...changedEntries
1470
1861
  ]);
1471
1862
  if (!isWatch && !compilationClosed) {
1472
1863
  compilationClosed = true;
1473
1864
  await compilation.close?.();
1474
1865
  }
1475
1866
  });
1476
- api.transform({
1477
- test: /\.[cm]?[jt]sx?$/
1478
- }, async (context)=>{
1867
+ const transformModule = async (context, background)=>{
1868
+ const hot = hotUpdatesEnabled(context.environment);
1479
1869
  const isJs = /\.[cm]?js$/.test(context.resourcePath);
1480
1870
  if (isJs) {
1481
1871
  const contents = await javascriptTransformer.transformData(context.resourcePath, context.code, {
1482
1872
  skipLinker: false,
1483
1873
  sideEffects: ()=>Promise.resolve(false)
1484
1874
  });
1875
+ const code = transformCrossThread(transformWorklets(Buffer.from(contents).toString(), context.resourcePath), context.resourcePath, {
1876
+ root: api.context.rootPath
1877
+ });
1878
+ if (!background) return {
1879
+ code
1880
+ };
1881
+ const wrapped = wrapPureClasses(code, context.resourcePath);
1485
1882
  return {
1486
- code: transformWorklets(Buffer.from(contents).toString(), context.resourcePath)
1883
+ code: hot && backgroundEntries.isEntryFile(context.resourcePath) ? addEntryHotAccept(wrapped) : wrapped
1487
1884
  };
1488
1885
  }
1489
1886
  const content = typeScriptFileCache.get(context.resourcePath);
1490
1887
  if (!content) throw new Error(`No compiled output found for ${context.resourcePath}`);
1491
1888
  for (const dependency of componentResourceDependencies.get(context.resourcePath) ?? [])context.addDependency(dependency);
1492
- const code = 'string' == typeof content ? content : Buffer.from(content).toString();
1889
+ const code = buildTransformedCode({
1890
+ code: 'string' == typeof content ? content : Buffer.from(content).toString(),
1891
+ resourcePath: context.resourcePath,
1892
+ stylesheets: moduleStylesheets.get(context.resourcePath),
1893
+ root: api.context.rootPath,
1894
+ hot: hot && !background ? {
1895
+ entry: appEntries.has(node_path.normalize(context.resourcePath)),
1896
+ source: ()=>node_fs.readFileSync(context.resourcePath, 'utf-8')
1897
+ } : void 0
1898
+ });
1899
+ if (!background) return {
1900
+ code
1901
+ };
1493
1902
  return {
1494
- code: buildTransformedCode({
1495
- code,
1496
- resourcePath: context.resourcePath,
1497
- componentStyles: componentStylesCache.get(context.resourcePath),
1498
- scopeInfo: componentScopeIds.get(context.resourcePath)
1499
- })
1903
+ code: wrapPureClasses(Buffer.from(await javascriptTransformer.transformData(context.resourcePath, code, {
1904
+ skipLinker: true,
1905
+ sideEffects: ()=>Promise.resolve(false)
1906
+ })).toString(), context.resourcePath)
1500
1907
  };
1908
+ };
1909
+ api.transform({
1910
+ test: /\.[cm]?[jt]sx?$/,
1911
+ issuerLayer: LAYERS.MAIN_THREAD
1912
+ }, (context)=>transformModule(context, false));
1913
+ api.transform({
1914
+ test: /\.[cm]?[jt]sx?$/,
1915
+ issuerLayer: LAYERS.BACKGROUND
1916
+ }, (context)=>transformModule(context, true));
1917
+ };
1918
+ const background_entry_RUNTIME_MODULE = '@blotch/angular-lynx';
1919
+ const createBackgroundEntries = ({ rootPath, findLibraryModules = findLibraryBackgroundModules })=>{
1920
+ const cacheDir = node_path.join(rootPath, 'node_modules', '.cache', 'angular-lynx', 'background-entries');
1921
+ const files = new Map();
1922
+ let libraryModules;
1923
+ const getLibraryModules = ()=>libraryModules ??= findLibraryModules(rootPath);
1924
+ let modules = new Set();
1925
+ const rebuilds = new Map();
1926
+ return {
1927
+ fileFor: (environmentName, entryName)=>{
1928
+ const file = node_path.join(cacheDir, environmentName, `${entryName}.js`);
1929
+ let environmentFiles = files.get(environmentName);
1930
+ if (!environmentFiles) {
1931
+ environmentFiles = new Set();
1932
+ files.set(environmentName, environmentFiles);
1933
+ }
1934
+ environmentFiles.add(file);
1935
+ if (!node_fs.existsSync(file)) writeIfChanged(file, renderBackgroundEntry({
1936
+ modules: []
1937
+ }));
1938
+ return file;
1939
+ },
1940
+ isBackgroundModule: (file)=>modules.has(file),
1941
+ isEntryFile: (file)=>[
1942
+ ...files.values()
1943
+ ].some((environmentFiles)=>environmentFiles.has(file)),
1944
+ update: (environmentName, { appModules, watch, rebuilt })=>{
1945
+ modules = new Set([
1946
+ ...getLibraryModules(),
1947
+ ...[
1948
+ ...appModules
1949
+ ].sort()
1950
+ ]);
1951
+ const rebuild = (rebuilds.get(environmentName) ?? 0) + (watch && rebuilt ? 1 : 0);
1952
+ rebuilds.set(environmentName, rebuild);
1953
+ const contents = renderBackgroundEntry({
1954
+ modules: [
1955
+ ...modules
1956
+ ],
1957
+ rebuild: watch ? rebuild : void 0
1958
+ });
1959
+ return [
1960
+ ...files.get(environmentName) ?? []
1961
+ ].filter((file)=>writeIfChanged(file, contents));
1962
+ }
1963
+ };
1964
+ };
1965
+ const renderBackgroundEntry = ({ modules, rebuild })=>{
1966
+ const lines = [
1967
+ "// The background thread's entry, generated by",
1968
+ '// @blotch/rsbuild-plugin-angular-lynx (see background-entry.ts there).',
1969
+ '// Angular runs on the main thread only, so the background thread loads the',
1970
+ '// runtime and the modules that call backgroundFn(), createMainThreadRef()',
1971
+ '// or new MainThreadRef() at module level.',
1972
+ `import { ɵstartBackgroundThread } from '${background_entry_RUNTIME_MODULE}';`,
1973
+ ...modules.map((file)=>`import ${JSON.stringify(file)};`),
1974
+ 'ɵstartBackgroundThread();'
1975
+ ];
1976
+ if (void 0 !== rebuild) lines.push(`// Rebuild ${rebuild}`);
1977
+ return `${lines.join('\n')}\n`;
1978
+ };
1979
+ const writeIfChanged = (file, contents)=>{
1980
+ if (node_fs.existsSync(file) && node_fs.readFileSync(file, 'utf8') === contents) return false;
1981
+ node_fs.mkdirSync(node_path.dirname(file), {
1982
+ recursive: true
1501
1983
  });
1984
+ node_fs.writeFileSync(file, contents);
1985
+ return true;
1986
+ };
1987
+ const JS_FILE = /\.[cm]?js$/;
1988
+ const findLibraryBackgroundModules = (rootPath)=>{
1989
+ const found = new Set();
1990
+ const visited = new Set();
1991
+ const visit = (packageDir, isApp)=>{
1992
+ const manifest = readManifest(packageDir);
1993
+ if (!manifest) return;
1994
+ const names = new Set([
1995
+ ...Object.keys(manifest.dependencies ?? {}),
1996
+ ...Object.keys(manifest.optionalDependencies ?? {}),
1997
+ ...Object.keys(manifest.peerDependencies ?? {}),
1998
+ ...isApp ? Object.keys(manifest.devDependencies ?? {}) : []
1999
+ ]);
2000
+ names.delete(background_entry_RUNTIME_MODULE);
2001
+ for (const name of names){
2002
+ const dependencyDir = resolvePackageDir(name, packageDir);
2003
+ if (!dependencyDir || visited.has(dependencyDir)) continue;
2004
+ visited.add(dependencyDir);
2005
+ const dependency = readManifest(dependencyDir);
2006
+ if (dependency && dependsOnRuntime(dependency)) {
2007
+ for (const file of listJsFiles(dependencyDir))if (definesCrossThreadValues(node_fs.readFileSync(file, 'utf8'), file)) found.add(file);
2008
+ }
2009
+ visit(dependencyDir, false);
2010
+ }
2011
+ };
2012
+ visit(rootPath, true);
2013
+ return [
2014
+ ...found
2015
+ ].sort();
1502
2016
  };
2017
+ const readManifest = (packageDir)=>{
2018
+ try {
2019
+ return JSON.parse(node_fs.readFileSync(node_path.join(packageDir, 'package.json'), 'utf8'));
2020
+ } catch {
2021
+ return;
2022
+ }
2023
+ };
2024
+ const dependsOnRuntime = (manifest)=>[
2025
+ manifest.dependencies,
2026
+ manifest.optionalDependencies,
2027
+ manifest.peerDependencies
2028
+ ].some((dependencies)=>dependencies?.[background_entry_RUNTIME_MODULE] !== void 0);
2029
+ const resolvePackageDir = (name, fromDir)=>{
2030
+ for(let dir = fromDir;; dir = node_path.dirname(dir)){
2031
+ const candidate = node_path.join(dir, 'node_modules', name);
2032
+ if (node_fs.existsSync(node_path.join(candidate, 'package.json'))) return node_fs.realpathSync(candidate);
2033
+ if (node_path.dirname(dir) === dir) return;
2034
+ }
2035
+ };
2036
+ const listJsFiles = (packageDir)=>node_fs.readdirSync(packageDir, {
2037
+ recursive: true,
2038
+ encoding: 'utf8'
2039
+ }).filter((file)=>JS_FILE.test(file) && !file.split(node_path.sep).includes('node_modules')).map((file)=>node_path.join(packageDir, file));
1503
2040
  const applyCSS = (api, options)=>{
1504
2041
  const { enableRemoveCSSScope, enableCSSSelector, enableCSSInvalidation, targetSdkVersion } = options;
1505
2042
  api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
@@ -1525,23 +2062,23 @@ const applyCSS = (api, options)=>{
1525
2062
  const mainRule = rule.oneOf(mainRuleName);
1526
2063
  const parentRuleEntries = rule.entries();
1527
2064
  removeLightningCSS(mainRule);
1528
- mainRule.issuerLayer(LAYERS.BACKGROUND).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
2065
+ mainRule.issuerLayer(LAYERS.MAIN_THREAD).use(CHAIN_ID.USE.MINI_CSS_EXTRACT).loader(CssExtractRspackPlugin.loader).end();
1529
2066
  const uses = mainRule.uses.entries() ?? {};
1530
2067
  const ruleEntries = mainRule.entries();
1531
2068
  const cssLoader = uses[CHAIN_ID.USE.CSS];
1532
2069
  if (!cssLoader) continue;
1533
2070
  const cssLoaderRule = cssLoader.entries();
1534
- const mainThreadLayerRule = chain.module.rule(`${ruleName}:${LAYERS.MAIN_THREAD}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.MAIN_THREAD);
1535
- if (void 0 !== parentRuleEntries.dependency) mainThreadLayerRule.merge({
2071
+ const backgroundLayerRule = chain.module.rule(`${ruleName}:${LAYERS.BACKGROUND}`).test(parentRuleEntries.test).merge(ruleEntries).issuerLayer(LAYERS.BACKGROUND);
2072
+ if (void 0 !== parentRuleEntries.dependency) backgroundLayerRule.merge({
1536
2073
  dependency: parentRuleEntries.dependency
1537
2074
  });
1538
- 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();
2075
+ backgroundLayerRule.use(CHAIN_ID.USE.IGNORE_CSS).loader(node_path.resolve(__dirname, './loaders/ignore-css-loader')).end().uses.merge(uses).delete(CHAIN_ID.USE.MINI_CSS_EXTRACT).delete(CHAIN_ID.USE.LIGHTNINGCSS).delete(CHAIN_ID.USE.CSS).end().use(CHAIN_ID.USE.CSS).after(CHAIN_ID.USE.IGNORE_CSS).merge(cssLoaderRule).options(normalizeCssLoaderOptions(cssLoaderRule.options, true)).end();
1539
2076
  }
1540
2077
  for (const ruleName of cssRules.filter((rule)=>chain.module.rules.has(rule))){
1541
2078
  const inlineRuleName = ruleName === CHAIN_ID.RULE.CSS ? CHAIN_ID.ONE_OF.CSS_INLINE : `${ruleName}-inline`;
1542
2079
  removeLightningCSS(chain.module.rule(ruleName).oneOf(inlineRuleName));
1543
2080
  }
1544
- if ('lynx' === environment.name && chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
2081
+ if (chain.module.rules.has(CHAIN_ID.RULE.FONT)) {
1545
2082
  const fontRule = chain.module.rule(CHAIN_ID.RULE.FONT);
1546
2083
  fontRule.oneOfs.clear();
1547
2084
  fontRule.type('asset/inline');
@@ -1597,6 +2134,21 @@ const applyGenerator = (api)=>{
1597
2134
  }
1598
2135
  });
1599
2136
  };
2137
+ const applyResolve = (api)=>{
2138
+ api.modifyRspackConfig((config)=>{
2139
+ const resolve = config.resolve ??= {};
2140
+ const conditions = (resolve.conditionNames ?? []).filter((condition)=>'require' !== condition);
2141
+ if (!conditions.includes('module')) conditions.splice(conditions.indexOf('import') + 1, 0, 'module');
2142
+ resolve.byDependency = {
2143
+ ...resolve.byDependency,
2144
+ esm: {
2145
+ ...resolve.byDependency?.['esm'],
2146
+ conditionNames: conditions
2147
+ }
2148
+ };
2149
+ });
2150
+ };
2151
+ const DEV_LOGS_PATH = '/__dev_logs';
1600
2152
  const RESET = '\x1b[0m';
1601
2153
  const GRAY = '\x1b[90m';
1602
2154
  const RED = '\x1b[31m';
@@ -1631,7 +2183,9 @@ const formatArg = (arg)=>{
1631
2183
  if ('string' == typeof arg) return arg;
1632
2184
  if ('object' == typeof arg && 'Error' === arg.__type) {
1633
2185
  const err = arg;
1634
- return err.stack || `${err.name}: ${err.message}`;
2186
+ const header = err.message ? `${err.name}: ${err.message}` : err.name;
2187
+ if (!err.stack) return header;
2188
+ return err.stack.startsWith(header) ? err.stack : `${header}\n${err.stack}`;
1635
2189
  }
1636
2190
  try {
1637
2191
  return JSON.stringify(arg, null, 2);
@@ -1639,28 +2193,19 @@ const formatArg = (arg)=>{
1639
2193
  return String(arg);
1640
2194
  }
1641
2195
  };
1642
- let logFile = null;
1643
- let logFilePath = '';
1644
- const getLogFilePath = ()=>logFilePath;
1645
- const ensureLogFile = ()=>{
1646
- if (!logFile) {
1647
- logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
1648
- logFile = node_fs.createWriteStream(logFilePath, {
1649
- flags: 'w'
1650
- });
1651
- }
1652
- return logFile;
1653
- };
1654
- const writeLog = (entry)=>{
1655
- const stream = ensureLogFile();
2196
+ const writeLog = (stream, entry)=>{
1656
2197
  const color = LEVEL_COLORS[entry.level] || WHITE;
1657
2198
  const label = LEVEL_LABELS[entry.level] || entry.level.toUpperCase();
1658
2199
  const time = formatTimestamp(entry.timestamp);
1659
2200
  const args = entry.args.map(formatArg).join(' ');
1660
2201
  stream.write(`${GRAY}${time}${RESET} ${color}${label}${RESET} ${args}\n`);
1661
2202
  };
1662
- const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1663
- if ('OPTIONS' === req.method && '/__dev_logs' === req.url) {
2203
+ const createDevLoggerMiddleware = (logFilePath)=>{
2204
+ const logFile = node_fs.createWriteStream(logFilePath, {
2205
+ flags: 'w'
2206
+ });
2207
+ return (req, res, next)=>{
2208
+ if ('OPTIONS' === req.method && req.url === DEV_LOGS_PATH) {
1664
2209
  res.writeHead(204, {
1665
2210
  'Access-Control-Allow-Origin': '*',
1666
2211
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
@@ -1669,7 +2214,7 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1669
2214
  res.end();
1670
2215
  return;
1671
2216
  }
1672
- if ('POST' !== req.method || '/__dev_logs' !== req.url) return void next();
2217
+ if ('POST' !== req.method || req.url !== DEV_LOGS_PATH) return void next();
1673
2218
  let body = '';
1674
2219
  req.on('data', (chunk)=>{
1675
2220
  body += chunk.toString();
@@ -1681,22 +2226,21 @@ const createDevLoggerMiddleware = ()=>(req, res, next)=>{
1681
2226
  res.end();
1682
2227
  try {
1683
2228
  const { logs } = JSON.parse(body);
1684
- for (const entry of logs)writeLog(entry);
2229
+ for (const entry of logs)writeLog(logFile, entry);
1685
2230
  } catch {}
1686
2231
  });
1687
2232
  };
1688
- const LOG_PORT_OFFSET = 1;
2233
+ };
2234
+ const FALLBACK_ORIGIN = 'http://localhost:3000';
1689
2235
  const applyDevLogger = (api)=>{
1690
2236
  api.modifyRspackConfig((rspackConfig)=>{
1691
2237
  if ('development' !== rspackConfig.mode) return rspackConfig;
1692
2238
  const publicPath = rspackConfig.output?.publicPath;
1693
- let logUrl = '';
2239
+ let origin = FALLBACK_ORIGIN;
1694
2240
  if ('string' == typeof publicPath && publicPath.startsWith('http')) try {
1695
- const parsed = new URL(publicPath);
1696
- const logPort = Number(parsed.port || 80) + LOG_PORT_OFFSET;
1697
- logUrl = `${parsed.protocol}//${parsed.hostname}:${logPort}/__dev_logs`;
2241
+ origin = new URL(publicPath).origin;
1698
2242
  } catch {}
1699
- if (!logUrl) logUrl = `http://localhost:${3000 + LOG_PORT_OFFSET}/__dev_logs`;
2243
+ const logUrl = `${origin}${DEV_LOGS_PATH}`;
1700
2244
  rspackConfig.plugins = rspackConfig.plugins || [];
1701
2245
  rspackConfig.plugins.push({
1702
2246
  name: 'lynx:angular:dev-logger-define',
@@ -1709,22 +2253,12 @@ const applyDevLogger = (api)=>{
1709
2253
  });
1710
2254
  return rspackConfig;
1711
2255
  });
1712
- api.onAfterStartDevServer(({ port })=>{
1713
- const logPort = port + LOG_PORT_OFFSET;
1714
- const middleware = createDevLoggerMiddleware();
1715
- const server = node_http.createServer((req, res)=>{
1716
- middleware(req, res, ()=>{
1717
- res.writeHead(404);
1718
- res.end();
1719
- });
1720
- });
1721
- server.listen(logPort, '0.0.0.0', ()=>{
1722
- console.log(`[dev-logger] Log server on :${logPort} → tail -f ${getLogFilePath()}`);
1723
- });
1724
- server.on('error', (err)=>{
1725
- if ('EADDRINUSE' === err.code) server.listen(logPort + 1, '0.0.0.0');
1726
- else console.warn('[dev-logger] Failed to start log server:', err.message);
1727
- });
2256
+ const logFilePath = node_path.resolve(process.cwd(), '.dev-logs');
2257
+ api.onBeforeStartDevServer(({ server })=>{
2258
+ server.middlewares.use(createDevLoggerMiddleware(logFilePath));
2259
+ });
2260
+ api.onAfterStartDevServer(()=>{
2261
+ console.log(`[dev-logger] LynxLogger output: tail -f ${logFilePath}`);
1728
2262
  });
1729
2263
  };
1730
2264
  const applySplitChunksRule = (api, options)=>{
@@ -1754,22 +2288,1869 @@ const applySplitChunksRule = (api, options)=>{
1754
2288
  return rspackConfig;
1755
2289
  });
1756
2290
  };
2291
+ const foldCalc = (value)=>{
2292
+ if (!value.includes('calc(')) return value;
2293
+ const parsed = postcss_value_parser(value);
2294
+ foldNodes(parsed.nodes);
2295
+ return parsed.toString();
2296
+ };
2297
+ const foldNodes = (nodes)=>{
2298
+ for (const node of nodes){
2299
+ if ('function' !== node.type) continue;
2300
+ const folded = 'calc' === node.value.toLowerCase() ? evaluate(node.nodes) : void 0;
2301
+ if (folded) Object.assign(node, {
2302
+ type: 'word',
2303
+ value: formatDimension(folded)
2304
+ });
2305
+ else foldNodes(node.nodes);
2306
+ }
2307
+ };
2308
+ const INFINITY_VALUE = 9999;
2309
+ const formatDimension = ({ value, unit })=>`${Number(value.toFixed(6))}${unit}`;
2310
+ const evaluate = (nodes)=>{
2311
+ const tokens = tokenize(nodes);
2312
+ if (!tokens) return;
2313
+ let position = 0;
2314
+ const peek = ()=>tokens[position];
2315
+ const factor = ()=>{
2316
+ const token = tokens[position++];
2317
+ return 'object' == typeof token ? token : void 0;
2318
+ };
2319
+ const term = ()=>{
2320
+ let left = factor();
2321
+ while(left && ('*' === peek() || '/' === peek())){
2322
+ const operator = tokens[position++];
2323
+ const right = factor();
2324
+ left = right && ('*' === operator ? multiply(left, right) : divide(left, right));
2325
+ }
2326
+ return left;
2327
+ };
2328
+ const expression = ()=>{
2329
+ let left = term();
2330
+ while(left && ('+' === peek() || '-' === peek())){
2331
+ const operator = tokens[position++];
2332
+ const right = term();
2333
+ left = right && add(left, '-' === operator ? negate(right) : right);
2334
+ }
2335
+ return left;
2336
+ };
2337
+ const result = expression();
2338
+ return position === tokens.length ? result : void 0;
2339
+ };
2340
+ const tokenize = (nodes)=>{
2341
+ const tokens = [];
2342
+ for (const node of nodes){
2343
+ if ('space' === node.type || 'comment' === node.type) continue;
2344
+ if ('function' === node.type) {
2345
+ const name = node.value.toLowerCase();
2346
+ const group = 'calc' === name || '' === name ? evaluate(node.nodes) : void 0;
2347
+ if (!group) return;
2348
+ tokens.push(group);
2349
+ continue;
2350
+ }
2351
+ if ('word' !== node.type) return;
2352
+ if ([
2353
+ '+',
2354
+ '-',
2355
+ '*',
2356
+ '/'
2357
+ ].includes(node.value)) {
2358
+ tokens.push(node.value);
2359
+ continue;
2360
+ }
2361
+ if (/^-?infinity$/i.test(node.value)) {
2362
+ tokens.push({
2363
+ value: node.value.startsWith('-') ? -INFINITY_VALUE : INFINITY_VALUE,
2364
+ unit: ''
2365
+ });
2366
+ continue;
2367
+ }
2368
+ const unit = postcss_value_parser.unit(node.value);
2369
+ if (!unit) return;
2370
+ tokens.push({
2371
+ value: Number(unit.number),
2372
+ unit: unit.unit.toLowerCase()
2373
+ });
2374
+ }
2375
+ return tokens;
2376
+ };
2377
+ const negate = ({ value, unit })=>({
2378
+ value: -value,
2379
+ unit
2380
+ });
2381
+ const add = (a, b)=>a.unit === b.unit ? {
2382
+ value: a.value + b.value,
2383
+ unit: a.unit
2384
+ } : void 0;
2385
+ const multiply = (a, b)=>{
2386
+ if (a.unit && b.unit) return;
2387
+ return {
2388
+ value: a.value * b.value,
2389
+ unit: a.unit || b.unit
2390
+ };
2391
+ };
2392
+ const divide = (a, b)=>b.unit || 0 === b.value ? void 0 : {
2393
+ value: a.value / b.value,
2394
+ unit: a.unit
2395
+ };
2396
+ const clampHugeNumbers = (value)=>value.replace(/(-?)\d*\.?\d+e\+?(\d+)([a-z%]*)/gi, (match, sign, exponent, unit)=>Number(exponent) >= 7 ? `${sign}${INFINITY_VALUE}${unit}` : match);
2397
+ const COLOR_FUNCTIONS = new Set([
2398
+ 'rgb',
2399
+ 'rgba',
2400
+ 'hsl',
2401
+ 'hsla',
2402
+ 'hwb',
2403
+ 'lab',
2404
+ 'lch',
2405
+ 'oklab',
2406
+ 'oklch',
2407
+ 'color-mix'
2408
+ ]);
2409
+ const LOWERED_FUNCTIONS = new Set([
2410
+ ...COLOR_FUNCTIONS
2411
+ ].filter((name)=>'rgb' !== name && 'rgba' !== name));
2412
+ const NAMED_COLORS = {
2413
+ aliceblue: 'f0f8ff',
2414
+ antiquewhite: 'faebd7',
2415
+ aqua: '00ffff',
2416
+ aquamarine: '7fffd4',
2417
+ azure: 'f0ffff',
2418
+ beige: 'f5f5dc',
2419
+ bisque: 'ffe4c4',
2420
+ black: '000000',
2421
+ blanchedalmond: 'ffebcd',
2422
+ blue: '0000ff',
2423
+ blueviolet: '8a2be2',
2424
+ brown: 'a52a2a',
2425
+ burlywood: 'deb887',
2426
+ cadetblue: '5f9ea0',
2427
+ chartreuse: '7fff00',
2428
+ chocolate: 'd2691e',
2429
+ coral: 'ff7f50',
2430
+ cornflowerblue: '6495ed',
2431
+ cornsilk: 'fff8dc',
2432
+ crimson: 'dc143c',
2433
+ cyan: '00ffff',
2434
+ darkblue: '00008b',
2435
+ darkcyan: '008b8b',
2436
+ darkgoldenrod: 'b8860b',
2437
+ darkgray: 'a9a9a9',
2438
+ darkgreen: '006400',
2439
+ darkgrey: 'a9a9a9',
2440
+ darkkhaki: 'bdb76b',
2441
+ darkmagenta: '8b008b',
2442
+ darkolivegreen: '556b2f',
2443
+ darkorange: 'ff8c00',
2444
+ darkorchid: '9932cc',
2445
+ darkred: '8b0000',
2446
+ darksalmon: 'e9967a',
2447
+ darkseagreen: '8fbc8f',
2448
+ darkslateblue: '483d8b',
2449
+ darkslategray: '2f4f4f',
2450
+ darkslategrey: '2f4f4f',
2451
+ darkturquoise: '00ced1',
2452
+ darkviolet: '9400d3',
2453
+ deeppink: 'ff1493',
2454
+ deepskyblue: '00bfff',
2455
+ dimgray: '696969',
2456
+ dimgrey: '696969',
2457
+ dodgerblue: '1e90ff',
2458
+ firebrick: 'b22222',
2459
+ floralwhite: 'fffaf0',
2460
+ forestgreen: '228b22',
2461
+ fuchsia: 'ff00ff',
2462
+ gainsboro: 'dcdcdc',
2463
+ ghostwhite: 'f8f8ff',
2464
+ gold: 'ffd700',
2465
+ goldenrod: 'daa520',
2466
+ gray: '808080',
2467
+ green: '008000',
2468
+ greenyellow: 'adff2f',
2469
+ grey: '808080',
2470
+ honeydew: 'f0fff0',
2471
+ hotpink: 'ff69b4',
2472
+ indianred: 'cd5c5c',
2473
+ indigo: '4b0082',
2474
+ ivory: 'fffff0',
2475
+ khaki: 'f0e68c',
2476
+ lavender: 'e6e6fa',
2477
+ lavenderblush: 'fff0f5',
2478
+ lawngreen: '7cfc00',
2479
+ lemonchiffon: 'fffacd',
2480
+ lightblue: 'add8e6',
2481
+ lightcoral: 'f08080',
2482
+ lightcyan: 'e0ffff',
2483
+ lightgoldenrodyellow: 'fafad2',
2484
+ lightgray: 'd3d3d3',
2485
+ lightgreen: '90ee90',
2486
+ lightgrey: 'd3d3d3',
2487
+ lightpink: 'ffb6c1',
2488
+ lightsalmon: 'ffa07a',
2489
+ lightseagreen: '20b2aa',
2490
+ lightskyblue: '87cefa',
2491
+ lightslategray: '778899',
2492
+ lightslategrey: '778899',
2493
+ lightsteelblue: 'b0c4de',
2494
+ lightyellow: 'ffffe0',
2495
+ lime: '00ff00',
2496
+ limegreen: '32cd32',
2497
+ linen: 'faf0e6',
2498
+ magenta: 'ff00ff',
2499
+ maroon: '800000',
2500
+ mediumaquamarine: '66cdaa',
2501
+ mediumblue: '0000cd',
2502
+ mediumorchid: 'ba55d3',
2503
+ mediumpurple: '9370db',
2504
+ mediumseagreen: '3cb371',
2505
+ mediumslateblue: '7b68ee',
2506
+ mediumspringgreen: '00fa9a',
2507
+ mediumturquoise: '48d1cc',
2508
+ mediumvioletred: 'c71585',
2509
+ midnightblue: '191970',
2510
+ mintcream: 'f5fffa',
2511
+ mistyrose: 'ffe4e1',
2512
+ moccasin: 'ffe4b5',
2513
+ navajowhite: 'ffdead',
2514
+ navy: '000080',
2515
+ oldlace: 'fdf5e6',
2516
+ olive: '808000',
2517
+ olivedrab: '6b8e23',
2518
+ orange: 'ffa500',
2519
+ orangered: 'ff4500',
2520
+ orchid: 'da70d6',
2521
+ palegoldenrod: 'eee8aa',
2522
+ palegreen: '98fb98',
2523
+ paleturquoise: 'afeeee',
2524
+ palevioletred: 'db7093',
2525
+ papayawhip: 'ffefd5',
2526
+ peachpuff: 'ffdab9',
2527
+ peru: 'cd853f',
2528
+ pink: 'ffc0cb',
2529
+ plum: 'dda0dd',
2530
+ powderblue: 'b0e0e6',
2531
+ purple: '800080',
2532
+ rebeccapurple: '663399',
2533
+ red: 'ff0000',
2534
+ rosybrown: 'bc8f8f',
2535
+ royalblue: '4169e1',
2536
+ saddlebrown: '8b4513',
2537
+ salmon: 'fa8072',
2538
+ sandybrown: 'f4a460',
2539
+ seagreen: '2e8b57',
2540
+ seashell: 'fff5ee',
2541
+ sienna: 'a0522d',
2542
+ silver: 'c0c0c0',
2543
+ skyblue: '87ceeb',
2544
+ slateblue: '6a5acd',
2545
+ slategray: '708090',
2546
+ slategrey: '708090',
2547
+ snow: 'fffafa',
2548
+ springgreen: '00ff7f',
2549
+ steelblue: '4682b4',
2550
+ tan: 'd2b48c',
2551
+ teal: '008080',
2552
+ thistle: 'd8bfd8',
2553
+ tomato: 'ff6347',
2554
+ turquoise: '40e0d0',
2555
+ violet: 'ee82ee',
2556
+ wheat: 'f5deb3',
2557
+ white: 'ffffff',
2558
+ whitesmoke: 'f5f5f5',
2559
+ yellow: 'ffff00',
2560
+ yellowgreen: '9acd32'
2561
+ };
2562
+ const lowerColors = (value, channelVariables = new Set())=>{
2563
+ if (![
2564
+ ...LOWERED_FUNCTIONS
2565
+ ].some((name)=>value.includes(`${name}(`))) return value;
2566
+ const parsed = postcss_value_parser(value);
2567
+ rewriteColorFunctions(parsed.nodes, channelVariables);
2568
+ return parsed.toString();
2569
+ };
2570
+ const rewriteColorFunctions = (nodes, channelVariables)=>{
2571
+ for (const node of nodes){
2572
+ if ('function' !== node.type) continue;
2573
+ const name = node.value.toLowerCase();
2574
+ const color = LOWERED_FUNCTIONS.has(name) ? parseColor(node) : void 0;
2575
+ if (color) {
2576
+ Object.assign(node, {
2577
+ type: 'word',
2578
+ value: formatRgb(color)
2579
+ });
2580
+ continue;
2581
+ }
2582
+ rewriteColorFunctions(node.nodes, channelVariables);
2583
+ const mixed = 'color-mix' === name ? mixWithChannels(node.nodes, channelVariables) : void 0;
2584
+ if (mixed) Object.assign(node, {
2585
+ type: 'word',
2586
+ value: mixed
2587
+ });
2588
+ }
2589
+ };
2590
+ const mixWithChannels = (nodes, channelVariables)=>{
2591
+ const parts = splitByComma(nodes);
2592
+ if (3 !== parts.length) return;
2593
+ const [, first, second] = parts;
2594
+ const variable = first.find((node)=>'function' === node.type && 'var' === node.value.toLowerCase());
2595
+ const name = variable?.nodes[0]?.value;
2596
+ const percentage = first.filter((node)=>node !== variable).map((node)=>postcss_value_parser.unit(node.value) || {
2597
+ number: '',
2598
+ unit: ''
2599
+ });
2600
+ const isTransparent = 1 === second.length && 'transparent' === second[0].value.toLowerCase();
2601
+ if (!name || !channelVariables.has(name) || 1 !== variable.nodes.length || !isTransparent || percentage.length > 1 || 1 === percentage.length && percentage[0]?.unit !== '%') return;
2602
+ const alpha = percentage[0] ? Number(percentage[0].number) / 100 : 0.5;
2603
+ return `rgba(var(${name}-rgb), ${Number(alpha.toFixed(4))})`;
2604
+ };
2605
+ const formatRgb = ({ r, g, b, alpha })=>{
2606
+ const channel = (value)=>Math.round(255 * clamp01(value));
2607
+ const channels = `${channel(r)}, ${channel(g)}, ${channel(b)}`;
2608
+ const a = Number(clamp01(alpha).toFixed(4));
2609
+ return 1 === a ? `rgb(${channels})` : `rgba(${channels}, ${a})`;
2610
+ };
2611
+ const clamp01 = (value)=>Math.min(1, Math.max(0, value));
2612
+ const parseColor = (node)=>{
2613
+ if ('word' === node.type) {
2614
+ const word = node.value.toLowerCase();
2615
+ if ('transparent' === word) return {
2616
+ r: 0,
2617
+ g: 0,
2618
+ b: 0,
2619
+ alpha: 0
2620
+ };
2621
+ const named = NAMED_COLORS[word];
2622
+ return parseHex('string' == typeof named ? `#${named}` : word);
2623
+ }
2624
+ if ('function' !== node.type) return;
2625
+ const name = node.value.toLowerCase();
2626
+ if (!COLOR_FUNCTIONS.has(name)) return;
2627
+ if ('color-mix' === name) return parseColorMix(node.nodes);
2628
+ const args = colorArguments(node.nodes);
2629
+ if (!args) return;
2630
+ const { channels, alpha } = args;
2631
+ switch(name){
2632
+ case 'rgb':
2633
+ case 'rgba':
2634
+ return withAlpha(rgbFromChannels(channels.map((c)=>colors_channel(c, 255))), alpha);
2635
+ case 'hsl':
2636
+ case 'hsla':
2637
+ return withAlpha(hslToRgb(angle(channels[0]), percent(channels[1]), percent(channels[2])), alpha);
2638
+ case 'hwb':
2639
+ return withAlpha(hwbToRgb(angle(channels[0]), percent(channels[1]), percent(channels[2])), alpha);
2640
+ case 'lab':
2641
+ return withAlpha(labToRgb([
2642
+ colors_channel(channels[0], 100),
2643
+ colors_channel(channels[1], 125),
2644
+ colors_channel(channels[2], 125)
2645
+ ]), alpha);
2646
+ case 'lch':
2647
+ return withAlpha(labToRgb(polarToRectangular([
2648
+ colors_channel(channels[0], 100),
2649
+ colors_channel(channels[1], 150),
2650
+ angle(channels[2])
2651
+ ])), alpha);
2652
+ case 'oklab':
2653
+ return withAlpha(oklabToRgb([
2654
+ colors_channel(channels[0], 1),
2655
+ colors_channel(channels[1], 0.4),
2656
+ colors_channel(channels[2], 0.4)
2657
+ ]), alpha);
2658
+ default:
2659
+ return withAlpha(oklabToRgb(polarToRectangular([
2660
+ colors_channel(channels[0], 1),
2661
+ colors_channel(channels[1], 0.4),
2662
+ angle(channels[2])
2663
+ ])), alpha);
2664
+ }
2665
+ };
2666
+ const rgbFromChannels = ([r, g, b])=>({
2667
+ r: r / 255,
2668
+ g: g / 255,
2669
+ b: b / 255,
2670
+ alpha: 1
2671
+ });
2672
+ const withAlpha = (color, alpha)=>({
2673
+ ...color,
2674
+ alpha: void 0 === alpha ? 1 : colors_channel(alpha, 1)
2675
+ });
2676
+ const parseHex = (word)=>{
2677
+ const match = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i.exec(word);
2678
+ if (!match) return;
2679
+ let hex = match[1];
2680
+ if (hex.length <= 4) hex = [
2681
+ ...hex
2682
+ ].map((digit)=>digit + digit).join('');
2683
+ const byte = (index)=>Number.parseInt(hex.slice(2 * index, 2 * index + 2), 16) / 255;
2684
+ return {
2685
+ r: byte(0),
2686
+ g: byte(1),
2687
+ b: byte(2),
2688
+ alpha: 8 === hex.length ? byte(3) : 1
2689
+ };
2690
+ };
2691
+ const colorArguments = (nodes)=>{
2692
+ const words = [];
2693
+ let alpha;
2694
+ let afterSlash = false;
2695
+ for (const node of nodes)if ('space' !== node.type && 'comment' !== node.type) {
2696
+ if ('div' === node.type) {
2697
+ afterSlash ||= '/' === node.value;
2698
+ continue;
2699
+ }
2700
+ if ('word' !== node.type) return;
2701
+ if (afterSlash) alpha = node.value;
2702
+ else words.push(node.value);
2703
+ }
2704
+ if (void 0 === alpha && 4 === words.length) alpha = words.pop();
2705
+ if (3 !== words.length || ![
2706
+ ...words,
2707
+ ...void 0 === alpha ? [] : [
2708
+ alpha
2709
+ ]
2710
+ ].every(isNumeric)) return;
2711
+ return {
2712
+ channels: words,
2713
+ alpha
2714
+ };
2715
+ };
2716
+ const isNumeric = (word)=>'none' === word.toLowerCase() || false !== postcss_value_parser.unit(word);
2717
+ const colors_channel = (word, full)=>{
2718
+ const { number, unit } = postcss_value_parser.unit(word) || {
2719
+ number: '0',
2720
+ unit: ''
2721
+ };
2722
+ return '%' === unit ? Number(number) / 100 * full : Number(number);
2723
+ };
2724
+ const percent = (word)=>colors_channel(word, 100) / 100;
2725
+ const angle = (word)=>{
2726
+ const { number, unit } = postcss_value_parser.unit(word) || {
2727
+ number: '0',
2728
+ unit: ''
2729
+ };
2730
+ const value = Number(number);
2731
+ switch(unit.toLowerCase()){
2732
+ case 'rad':
2733
+ return 180 * value / Math.PI;
2734
+ case 'grad':
2735
+ return 0.9 * value;
2736
+ case 'turn':
2737
+ return 360 * value;
2738
+ default:
2739
+ return value;
2740
+ }
2741
+ };
2742
+ const hslToRgb = (hue, saturation, lightness)=>{
2743
+ const h = (hue % 360 + 360) % 360;
2744
+ const f = (n)=>{
2745
+ const k = (n + h / 30) % 12;
2746
+ const a = saturation * Math.min(lightness, 1 - lightness);
2747
+ return lightness - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
2748
+ };
2749
+ return {
2750
+ r: f(0),
2751
+ g: f(8),
2752
+ b: f(4),
2753
+ alpha: 1
2754
+ };
2755
+ };
2756
+ const hwbToRgb = (hue, whiteness, blackness)=>{
2757
+ if (whiteness + blackness >= 1) {
2758
+ const gray = whiteness / (whiteness + blackness);
2759
+ return {
2760
+ r: gray,
2761
+ g: gray,
2762
+ b: gray,
2763
+ alpha: 1
2764
+ };
2765
+ }
2766
+ const { r, g, b } = hslToRgb(hue, 1, 0.5);
2767
+ const scale = (value)=>value * (1 - whiteness - blackness) + whiteness;
2768
+ return {
2769
+ r: scale(r),
2770
+ g: scale(g),
2771
+ b: scale(b),
2772
+ alpha: 1
2773
+ };
2774
+ };
2775
+ const polarToRectangular = ([lightness, chroma, hue])=>{
2776
+ const radians = hue * Math.PI / 180;
2777
+ return [
2778
+ lightness,
2779
+ chroma * Math.cos(radians),
2780
+ chroma * Math.sin(radians)
2781
+ ];
2782
+ };
2783
+ const rectangularToPolar = ([lightness, a, b])=>{
2784
+ const hue = 180 * Math.atan2(b, a) / Math.PI;
2785
+ return [
2786
+ lightness,
2787
+ Math.hypot(a, b),
2788
+ (hue + 360) % 360
2789
+ ];
2790
+ };
2791
+ const gammaEncode = (value)=>{
2792
+ const sign = value < 0 ? -1 : 1;
2793
+ const abs = Math.abs(value);
2794
+ return abs > 0.0031308 ? sign * (1.055 * abs ** (1 / 2.4) - 0.055) : 12.92 * value;
2795
+ };
2796
+ const gammaDecode = (value)=>{
2797
+ const sign = value < 0 ? -1 : 1;
2798
+ const abs = Math.abs(value);
2799
+ return abs > 0.04045 ? sign * ((abs + 0.055) / 1.055) ** 2.4 : value / 12.92;
2800
+ };
2801
+ const oklabToLinearRgb = ([l, a, b])=>{
2802
+ const lms = [
2803
+ (l + 0.3963377773761749 * a + 0.2158037573099136 * b) ** 3,
2804
+ (l - 0.1055613458156586 * a - 0.0638541728258133 * b) ** 3,
2805
+ (l - 0.0894841775298119 * a - 1.2914855480194092 * b) ** 3
2806
+ ];
2807
+ return [
2808
+ 4.0767416360759583 * lms[0] - 3.3077115392580629 * lms[1] + 0.2309699031821043 * lms[2],
2809
+ -1.2684379732850315 * lms[0] + 2.6097573492876882 * lms[1] - 0.341319376002657 * lms[2],
2810
+ -0.0041960761386756 * lms[0] - 0.7034186179359363 * lms[1] + 1.7076146940746117 * lms[2]
2811
+ ];
2812
+ };
2813
+ const linearRgbToOklab = ([r, g, b])=>{
2814
+ const lms = [
2815
+ Math.cbrt(0.4122214694707629 * r + 0.5363325372617349 * g + 0.0514459932675022 * b),
2816
+ Math.cbrt(0.2119034958178251 * r + 0.6806995506452344 * g + 0.1073969535369406 * b),
2817
+ Math.cbrt(0.0883024591900564 * r + 0.2817188391361215 * g + 0.6299787016738223 * b)
2818
+ ];
2819
+ return [
2820
+ 0.2104542553 * lms[0] + 0.793617785 * lms[1] - 0.0040720468 * lms[2],
2821
+ 1.9779984951 * lms[0] - 2.428592205 * lms[1] + 0.4505937099 * lms[2],
2822
+ 0.0259040371 * lms[0] + 0.7827717662 * lms[1] - 0.808675766 * lms[2]
2823
+ ];
2824
+ };
2825
+ const encodeRgb = ([r, g, b])=>({
2826
+ r: gammaEncode(r),
2827
+ g: gammaEncode(g),
2828
+ b: gammaEncode(b),
2829
+ alpha: 1
2830
+ });
2831
+ const rgbToOklab = ({ r, g, b })=>linearRgbToOklab([
2832
+ gammaDecode(r),
2833
+ gammaDecode(g),
2834
+ gammaDecode(b)
2835
+ ]);
2836
+ const isInGamut = ({ r, g, b })=>[
2837
+ r,
2838
+ g,
2839
+ b
2840
+ ].every((value)=>value >= -0.000001 && value <= 1 + 1e-6);
2841
+ const clip = ({ r, g, b, alpha })=>({
2842
+ r: clamp01(r),
2843
+ g: clamp01(g),
2844
+ b: clamp01(b),
2845
+ alpha
2846
+ });
2847
+ const oklabToRgb = (oklab)=>{
2848
+ const color = encodeRgb(oklabToLinearRgb(oklab));
2849
+ if (isInGamut(color)) return color;
2850
+ const [lightness, chroma, hue] = rectangularToPolar(oklab);
2851
+ if (lightness >= 1) return {
2852
+ r: 1,
2853
+ g: 1,
2854
+ b: 1,
2855
+ alpha: 1
2856
+ };
2857
+ if (lightness <= 0) return {
2858
+ r: 0,
2859
+ g: 0,
2860
+ b: 0,
2861
+ alpha: 1
2862
+ };
2863
+ const JND = 0.02;
2864
+ const EPSILON = 0.0001;
2865
+ const atChroma = (c)=>encodeRgb(oklabToLinearRgb(polarToRectangular([
2866
+ lightness,
2867
+ c,
2868
+ hue
2869
+ ])));
2870
+ const deltaE = (a, b)=>Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
2871
+ let clipped = clip(color);
2872
+ if (deltaE(rgbToOklab(clipped), oklab) < JND) return clipped;
2873
+ let min = 0;
2874
+ let max = chroma;
2875
+ let minInGamut = true;
2876
+ while(max - min > EPSILON){
2877
+ const c = (min + max) / 2;
2878
+ const current = atChroma(c);
2879
+ if (minInGamut && isInGamut(current)) {
2880
+ min = c;
2881
+ continue;
2882
+ }
2883
+ clipped = clip(current);
2884
+ const e = deltaE(rgbToOklab(clipped), polarToRectangular([
2885
+ lightness,
2886
+ c,
2887
+ hue
2888
+ ]));
2889
+ if (e < JND) {
2890
+ if (JND - e < EPSILON) break;
2891
+ minInGamut = false;
2892
+ min = c;
2893
+ } else max = c;
2894
+ }
2895
+ return clipped;
2896
+ };
2897
+ const labToRgb = ([l, a, b])=>{
2898
+ const k = 24389 / 27;
2899
+ const e = 216 / 24389;
2900
+ const fy = (l + 16) / 116;
2901
+ const fx = a / 500 + fy;
2902
+ const fz = fy - b / 200;
2903
+ const xyz = [
2904
+ (fx ** 3 > e ? fx ** 3 : (116 * fx - 16) / k) * 0.3457 / 0.3585,
2905
+ l > k * e ? fy ** 3 : l / k,
2906
+ (fz ** 3 > e ? fz ** 3 : (116 * fz - 16) / k) * 0.2958 / 0.3585
2907
+ ];
2908
+ const d65 = [
2909
+ 0.955473421488075 * xyz[0] - 0.02309845494876471 * xyz[1] + 0.06325924320057072 * xyz[2],
2910
+ -0.0283697093338637 * xyz[0] + 1.0099953980813041 * xyz[1] + 0.021041441191917323 * xyz[2],
2911
+ 0.012314014864481998 * xyz[0] - 0.020507649298898964 * xyz[1] + 1.330365926242124 * xyz[2]
2912
+ ];
2913
+ const linear = [
2914
+ 3.2409699419045226 * d65[0] - 1.537383177570094 * d65[1] - 0.4986107602930034 * d65[2],
2915
+ -0.9692436362808796 * d65[0] + 1.8759675015077202 * d65[1] + 0.04155505740717559 * d65[2],
2916
+ 0.05563007969699366 * d65[0] - 0.20397695888897652 * d65[1] + 1.0569715142428786 * d65[2]
2917
+ ];
2918
+ const color = encodeRgb(linear);
2919
+ return isInGamut(color) ? color : oklabToRgb(linearRgbToOklab(linear));
2920
+ };
2921
+ const parseColorMix = (nodes)=>{
2922
+ const parts = splitByComma(nodes);
2923
+ if (3 !== parts.length) return;
2924
+ const space = mixSpace(parts[0]);
2925
+ const first = mixColor(parts[1]);
2926
+ const second = mixColor(parts[2]);
2927
+ if (!space || !first || !second) return;
2928
+ let p1 = first.percentage;
2929
+ let p2 = second.percentage;
2930
+ if (void 0 === p1 && void 0 === p2) p1 = p2 = 50;
2931
+ else if (void 0 === p1) p1 = 100 - p2;
2932
+ else if (void 0 === p2) p2 = 100 - p1;
2933
+ const sum = p1 + p2;
2934
+ if (sum <= 0) return;
2935
+ const mixed = interpolate(space, first.color, second.color, p2 / sum);
2936
+ return {
2937
+ ...mixed,
2938
+ alpha: mixed.alpha * Math.min(sum, 100) / 100
2939
+ };
2940
+ };
2941
+ const splitByComma = (nodes)=>{
2942
+ const parts = [
2943
+ []
2944
+ ];
2945
+ for (const node of nodes)if ('div' === node.type && ',' === node.value) parts.push([]);
2946
+ else if ('space' !== node.type && 'comment' !== node.type) parts[parts.length - 1].push(node);
2947
+ return parts;
2948
+ };
2949
+ const MIX_SPACES = new Set([
2950
+ 'srgb',
2951
+ 'srgb-linear',
2952
+ 'oklab',
2953
+ 'oklch'
2954
+ ]);
2955
+ const mixSpace = (nodes)=>{
2956
+ const words = nodes.map((node)=>node.value.toLowerCase());
2957
+ const [keyword, space, ...hue] = words;
2958
+ const defaultHue = 0 === hue.length || 'shorter' === hue[0] && 'hue' === hue[1];
2959
+ return 'in' === keyword && MIX_SPACES.has(space) && defaultHue ? space : void 0;
2960
+ };
2961
+ const mixColor = (nodes)=>{
2962
+ let color;
2963
+ let percentage;
2964
+ for (const node of nodes){
2965
+ const unit = 'word' === node.type && postcss_value_parser.unit(node.value);
2966
+ if (unit && '%' === unit.unit && void 0 === percentage) percentage = Number(unit.number);
2967
+ else {
2968
+ if (color) return;
2969
+ color = parseColor(node);
2970
+ if (!color) return;
2971
+ }
2972
+ }
2973
+ return color && {
2974
+ color,
2975
+ percentage
2976
+ };
2977
+ };
2978
+ const interpolate = (space, from, to, weight)=>{
2979
+ const alpha = from.alpha * (1 - weight) + to.alpha * weight;
2980
+ if (0 === alpha) return {
2981
+ r: 0,
2982
+ g: 0,
2983
+ b: 0,
2984
+ alpha: 0
2985
+ };
2986
+ const blend = (a, b)=>(a * from.alpha * (1 - weight) + b * to.alpha * weight) / alpha;
2987
+ switch(space){
2988
+ case 'srgb':
2989
+ return {
2990
+ r: blend(from.r, to.r),
2991
+ g: blend(from.g, to.g),
2992
+ b: blend(from.b, to.b),
2993
+ alpha
2994
+ };
2995
+ case 'srgb-linear':
2996
+ return {
2997
+ ...encodeRgb([
2998
+ blend(gammaDecode(from.r), gammaDecode(to.r)),
2999
+ blend(gammaDecode(from.g), gammaDecode(to.g)),
3000
+ blend(gammaDecode(from.b), gammaDecode(to.b))
3001
+ ]),
3002
+ alpha
3003
+ };
3004
+ case 'oklab':
3005
+ {
3006
+ const a = rgbToOklab(from);
3007
+ const b = rgbToOklab(to);
3008
+ return {
3009
+ ...oklabToRgb([
3010
+ blend(a[0], b[0]),
3011
+ blend(a[1], b[1]),
3012
+ blend(a[2], b[2])
3013
+ ]),
3014
+ alpha
3015
+ };
3016
+ }
3017
+ default:
3018
+ {
3019
+ const a = rectangularToPolar(rgbToOklab(from));
3020
+ const b = rectangularToPolar(rgbToOklab(to));
3021
+ const powerless = (color, source)=>color[1] < 1e-4 || 0 === source.alpha;
3022
+ const hueA = powerless(a, from) ? b[2] : a[2];
3023
+ const hueB = powerless(b, to) ? a[2] : b[2];
3024
+ let delta = hueB - hueA;
3025
+ if (delta > 180) delta -= 360;
3026
+ else if (delta < -180) delta += 360;
3027
+ const hue = hueA + delta * weight;
3028
+ return {
3029
+ ...oklabToRgb(polarToRectangular([
3030
+ blend(a[0], b[0]),
3031
+ blend(a[1], b[1]),
3032
+ hue
3033
+ ])),
3034
+ alpha
3035
+ };
3036
+ }
3037
+ }
3038
+ };
3039
+ const LYNX_PROPERTIES = new Set([
3040
+ '-x-animation-color-interpolation',
3041
+ '-x-app-region',
3042
+ '-x-auto-font-size',
3043
+ '-x-auto-font-size-line-ranges',
3044
+ '-x-auto-font-size-preset-sizes',
3045
+ '-x-box-match-parent-size',
3046
+ '-x-box-propagate-min-constraints',
3047
+ '-x-caret-gradient',
3048
+ '-x-caret-height',
3049
+ '-x-caret-radius',
3050
+ '-x-caret-width',
3051
+ '-x-handle-color',
3052
+ '-x-handle-size',
3053
+ '-x-placeholder-color',
3054
+ '-x-placeholder-font-family',
3055
+ '-x-placeholder-font-size',
3056
+ '-x-placeholder-font-style',
3057
+ '-x-placeholder-font-weight',
3058
+ '-x-text-decoration-gap',
3059
+ '-x-text-decoration-width',
3060
+ 'adapt-font-size',
3061
+ 'align-content',
3062
+ 'align-items',
3063
+ 'align-self',
3064
+ 'animation',
3065
+ 'animation-delay',
3066
+ 'animation-direction',
3067
+ 'animation-duration',
3068
+ 'animation-fill-mode',
3069
+ 'animation-iteration-count',
3070
+ 'animation-name',
3071
+ 'animation-play-state',
3072
+ 'animation-timing-function',
3073
+ 'aspect-ratio',
3074
+ 'background',
3075
+ 'background-clip',
3076
+ 'background-color',
3077
+ 'background-image',
3078
+ 'background-origin',
3079
+ 'background-position',
3080
+ 'background-repeat',
3081
+ 'background-size',
3082
+ 'border',
3083
+ 'border-bottom',
3084
+ 'border-bottom-color',
3085
+ 'border-bottom-left-radius',
3086
+ 'border-bottom-right-radius',
3087
+ 'border-bottom-style',
3088
+ 'border-bottom-width',
3089
+ 'border-color',
3090
+ 'border-end-end-radius',
3091
+ 'border-end-start-radius',
3092
+ 'border-inline-end-color',
3093
+ 'border-inline-end-style',
3094
+ 'border-inline-end-width',
3095
+ 'border-inline-start-color',
3096
+ 'border-inline-start-style',
3097
+ 'border-inline-start-width',
3098
+ 'border-left',
3099
+ 'border-left-color',
3100
+ 'border-left-style',
3101
+ 'border-left-width',
3102
+ 'border-radius',
3103
+ 'border-right',
3104
+ 'border-right-color',
3105
+ 'border-right-style',
3106
+ 'border-right-width',
3107
+ 'border-start-end-radius',
3108
+ 'border-start-start-radius',
3109
+ 'border-style',
3110
+ 'border-top',
3111
+ 'border-top-color',
3112
+ 'border-top-left-radius',
3113
+ 'border-top-right-radius',
3114
+ 'border-top-style',
3115
+ 'border-top-width',
3116
+ 'border-width',
3117
+ 'bottom',
3118
+ 'box-shadow',
3119
+ 'box-sizing',
3120
+ 'caret-color',
3121
+ 'clip-path',
3122
+ 'color',
3123
+ 'column-gap',
3124
+ 'content',
3125
+ 'cursor',
3126
+ 'direction',
3127
+ 'display',
3128
+ 'enter-transition-name',
3129
+ 'exit-transition-name',
3130
+ 'filter',
3131
+ 'flex',
3132
+ 'flex-basis',
3133
+ 'flex-direction',
3134
+ 'flex-flow',
3135
+ 'flex-grow',
3136
+ 'flex-shrink',
3137
+ 'flex-wrap',
3138
+ 'font-family',
3139
+ 'font-feature-settings',
3140
+ 'font-optical-sizing',
3141
+ 'font-size',
3142
+ 'font-style',
3143
+ 'font-variation-settings',
3144
+ 'font-weight',
3145
+ 'gap',
3146
+ 'grid-auto-columns',
3147
+ 'grid-auto-flow',
3148
+ 'grid-auto-rows',
3149
+ 'grid-column',
3150
+ 'grid-column-end',
3151
+ 'grid-column-gap',
3152
+ 'grid-column-span',
3153
+ 'grid-column-start',
3154
+ 'grid-row',
3155
+ 'grid-row-end',
3156
+ 'grid-row-gap',
3157
+ 'grid-row-span',
3158
+ 'grid-row-start',
3159
+ 'grid-template-columns',
3160
+ 'grid-template-rows',
3161
+ 'height',
3162
+ 'hyphens',
3163
+ 'image-rendering',
3164
+ 'implicit-animation',
3165
+ 'inset-inline-end',
3166
+ 'inset-inline-start',
3167
+ 'justify-content',
3168
+ 'justify-items',
3169
+ 'justify-self',
3170
+ 'layout-animation-create-delay',
3171
+ 'layout-animation-create-duration',
3172
+ 'layout-animation-create-property',
3173
+ 'layout-animation-create-timing-function',
3174
+ 'layout-animation-delete-delay',
3175
+ 'layout-animation-delete-duration',
3176
+ 'layout-animation-delete-property',
3177
+ 'layout-animation-delete-timing-function',
3178
+ 'layout-animation-update-delay',
3179
+ 'layout-animation-update-duration',
3180
+ 'layout-animation-update-timing-function',
3181
+ 'left',
3182
+ 'letter-spacing',
3183
+ 'line-height',
3184
+ 'line-spacing',
3185
+ 'linear-cross-gravity',
3186
+ 'linear-direction',
3187
+ 'linear-gravity',
3188
+ 'linear-layout-gravity',
3189
+ 'linear-orientation',
3190
+ 'linear-weight',
3191
+ 'linear-weight-sum',
3192
+ 'list-cross-axis-gap',
3193
+ 'list-main-axis-gap',
3194
+ 'margin',
3195
+ 'margin-bottom',
3196
+ 'margin-inline-end',
3197
+ 'margin-inline-start',
3198
+ 'margin-left',
3199
+ 'margin-right',
3200
+ 'margin-top',
3201
+ 'mask',
3202
+ 'mask-clip',
3203
+ 'mask-composite',
3204
+ 'mask-image',
3205
+ 'mask-origin',
3206
+ 'mask-position',
3207
+ 'mask-repeat',
3208
+ 'mask-size',
3209
+ 'max-height',
3210
+ 'max-width',
3211
+ 'min-height',
3212
+ 'min-width',
3213
+ 'offset-distance',
3214
+ 'offset-path',
3215
+ 'offset-rotate',
3216
+ 'opacity',
3217
+ 'order',
3218
+ 'outline',
3219
+ 'outline-color',
3220
+ 'outline-style',
3221
+ 'outline-width',
3222
+ 'overflow',
3223
+ 'overflow-x',
3224
+ 'overflow-y',
3225
+ 'padding',
3226
+ 'padding-bottom',
3227
+ 'padding-inline-end',
3228
+ 'padding-inline-start',
3229
+ 'padding-left',
3230
+ 'padding-right',
3231
+ 'padding-top',
3232
+ 'pause-transition-name',
3233
+ 'perspective',
3234
+ 'pointer-events',
3235
+ 'position',
3236
+ 'relative-align-bottom',
3237
+ 'relative-align-inline-end',
3238
+ 'relative-align-inline-start',
3239
+ 'relative-align-left',
3240
+ 'relative-align-right',
3241
+ 'relative-align-top',
3242
+ 'relative-bottom-of',
3243
+ 'relative-center',
3244
+ 'relative-id',
3245
+ 'relative-inline-end-of',
3246
+ 'relative-inline-start-of',
3247
+ 'relative-layout-once',
3248
+ 'relative-left-of',
3249
+ 'relative-right-of',
3250
+ 'relative-top-of',
3251
+ 'resume-transition-name',
3252
+ 'right',
3253
+ 'row-gap',
3254
+ 'text-align',
3255
+ 'text-decoration',
3256
+ 'text-decoration-color',
3257
+ 'text-decoration-thickness',
3258
+ 'text-indent',
3259
+ 'text-overflow',
3260
+ 'text-shadow',
3261
+ 'text-stroke',
3262
+ 'text-stroke-color',
3263
+ 'text-stroke-width',
3264
+ 'top',
3265
+ 'transform',
3266
+ 'transform-origin',
3267
+ 'transition',
3268
+ 'transition-delay',
3269
+ 'transition-duration',
3270
+ 'transition-property',
3271
+ 'transition-timing-function',
3272
+ 'vertical-align',
3273
+ 'visibility',
3274
+ 'white-space',
3275
+ 'width',
3276
+ 'word-break',
3277
+ 'z-index'
3278
+ ]);
3279
+ const LYNX_KEYWORDS = new Map(Object.entries({
3280
+ '-x-animation-color-interpolation': [
3281
+ 'auto',
3282
+ 'srgb',
3283
+ 'linearrgb'
3284
+ ],
3285
+ '-x-app-region': [
3286
+ 'none',
3287
+ 'drag',
3288
+ 'no-drag'
3289
+ ],
3290
+ '-x-placeholder-font-style': [
3291
+ 'normal',
3292
+ 'italic',
3293
+ 'oblique'
3294
+ ],
3295
+ '-x-placeholder-font-weight': [
3296
+ 'normal',
3297
+ 'bold',
3298
+ '100',
3299
+ '200',
3300
+ '300',
3301
+ '400',
3302
+ '500',
3303
+ '600',
3304
+ '700',
3305
+ '800',
3306
+ '900'
3307
+ ],
3308
+ 'align-content': [
3309
+ 'flex-start',
3310
+ 'flex-end',
3311
+ 'center',
3312
+ 'stretch',
3313
+ 'space-between',
3314
+ 'space-around',
3315
+ 'start',
3316
+ 'end'
3317
+ ],
3318
+ 'box-sizing': [
3319
+ 'border-box',
3320
+ 'content-box',
3321
+ 'auto'
3322
+ ],
3323
+ direction: [
3324
+ 'normal',
3325
+ 'lynx-rtl',
3326
+ 'rtl',
3327
+ 'ltr'
3328
+ ],
3329
+ display: [
3330
+ 'none',
3331
+ 'flex',
3332
+ 'grid',
3333
+ 'linear',
3334
+ 'relative',
3335
+ 'block',
3336
+ 'auto',
3337
+ '-x-box'
3338
+ ],
3339
+ 'flex-direction': [
3340
+ 'column',
3341
+ 'row',
3342
+ 'row-reverse',
3343
+ 'column-reverse'
3344
+ ],
3345
+ 'flex-wrap': [
3346
+ 'wrap',
3347
+ 'nowrap',
3348
+ 'wrap-reverse'
3349
+ ],
3350
+ 'font-optical-sizing': [
3351
+ 'none',
3352
+ 'auto'
3353
+ ],
3354
+ 'font-style': [
3355
+ 'normal',
3356
+ 'italic',
3357
+ 'oblique'
3358
+ ],
3359
+ 'font-weight': [
3360
+ 'normal',
3361
+ 'bold',
3362
+ '100',
3363
+ '200',
3364
+ '300',
3365
+ '400',
3366
+ '500',
3367
+ '600',
3368
+ '700',
3369
+ '800',
3370
+ '900'
3371
+ ],
3372
+ hyphens: [
3373
+ 'none',
3374
+ 'manual',
3375
+ 'auto'
3376
+ ],
3377
+ 'image-rendering': [
3378
+ 'auto',
3379
+ 'crisp-edges',
3380
+ 'pixelated'
3381
+ ],
3382
+ 'justify-content': [
3383
+ 'flex-start',
3384
+ 'center',
3385
+ 'flex-end',
3386
+ 'space-between',
3387
+ 'space-around',
3388
+ 'space-evenly',
3389
+ 'stretch',
3390
+ 'start',
3391
+ 'end'
3392
+ ],
3393
+ 'linear-cross-gravity': [
3394
+ 'none',
3395
+ 'start',
3396
+ 'end',
3397
+ 'center',
3398
+ 'stretch'
3399
+ ],
3400
+ 'linear-gravity': [
3401
+ 'none',
3402
+ 'top',
3403
+ 'bottom',
3404
+ 'left',
3405
+ 'right',
3406
+ 'center-vertical',
3407
+ 'center-horizontal',
3408
+ 'space-between',
3409
+ 'start',
3410
+ 'end',
3411
+ 'center'
3412
+ ],
3413
+ 'linear-layout-gravity': [
3414
+ 'none',
3415
+ 'top',
3416
+ 'bottom',
3417
+ 'left',
3418
+ 'right',
3419
+ 'center-vertical',
3420
+ 'center-horizontal',
3421
+ 'fill-vertical',
3422
+ 'fill-horizontal',
3423
+ 'center',
3424
+ 'stretch',
3425
+ 'start',
3426
+ 'end'
3427
+ ],
3428
+ 'linear-orientation': [
3429
+ 'horizontal',
3430
+ 'vertical',
3431
+ 'horizontal-reverse',
3432
+ 'vertical-reverse',
3433
+ 'row',
3434
+ 'column',
3435
+ 'row-reverse',
3436
+ 'column-reverse'
3437
+ ],
3438
+ 'pointer-events': [
3439
+ 'auto',
3440
+ 'none'
3441
+ ],
3442
+ position: [
3443
+ 'absolute',
3444
+ 'relative',
3445
+ 'fixed',
3446
+ 'sticky'
3447
+ ],
3448
+ 'relative-center': [
3449
+ 'none',
3450
+ 'vertical',
3451
+ 'horizontal',
3452
+ 'both'
3453
+ ],
3454
+ 'text-align': [
3455
+ 'left',
3456
+ 'center',
3457
+ 'right',
3458
+ 'start',
3459
+ 'end',
3460
+ 'justify'
3461
+ ],
3462
+ 'text-overflow': [
3463
+ 'clip',
3464
+ 'ellipsis'
3465
+ ],
3466
+ visibility: [
3467
+ 'hidden',
3468
+ 'visible',
3469
+ 'none',
3470
+ 'collapse'
3471
+ ],
3472
+ 'white-space': [
3473
+ 'normal',
3474
+ 'nowrap'
3475
+ ],
3476
+ 'word-break': [
3477
+ 'normal',
3478
+ 'break-all',
3479
+ 'keep-all'
3480
+ ]
3481
+ }).map(([property, keywords])=>[
3482
+ property,
3483
+ new Set(keywords)
3484
+ ]));
3485
+ const COMPOSED_TRANSFORM = "translate3d(var(--tw-translate-x), var(--tw-translate-y), var(--tw-translate-z)) rotate(var(--tw-rotate)) scale(var(--tw-scale-x), var(--tw-scale-y)) var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)";
3486
+ const TRANSFORM_DEFAULTS = new Map([
3487
+ [
3488
+ '--tw-translate-x',
3489
+ '0'
3490
+ ],
3491
+ [
3492
+ '--tw-translate-y',
3493
+ '0'
3494
+ ],
3495
+ [
3496
+ '--tw-translate-z',
3497
+ '0'
3498
+ ],
3499
+ [
3500
+ '--tw-rotate',
3501
+ '0deg'
3502
+ ],
3503
+ [
3504
+ '--tw-scale-x',
3505
+ '1'
3506
+ ],
3507
+ [
3508
+ '--tw-scale-y',
3509
+ '1'
3510
+ ]
3511
+ ]);
3512
+ const lowerDeclarations = (rule, channelVariables = new Set())=>{
3513
+ const composed = [];
3514
+ for (const declaration of rule.nodes.slice())if ('decl' === declaration.type) {
3515
+ declaration.value = lowerColors(declaration.value, channelVariables);
3516
+ if (lowerTransformProperty(declaration)) {
3517
+ composed.push(declaration);
3518
+ continue;
3519
+ }
3520
+ lowerValue(declaration);
3521
+ expandLogical(declaration);
3522
+ }
3523
+ if (composed.length > 0) {
3524
+ composed[composed.length - 1].before({
3525
+ prop: 'transform',
3526
+ value: COMPOSED_TRANSFORM
3527
+ });
3528
+ for (const declaration of composed)declaration.remove();
3529
+ }
3530
+ dropUnsupportedProperties(rule);
3531
+ return {
3532
+ composesTransform: composed.length > 0
3533
+ };
3534
+ };
3535
+ const TRANSFORM_VARIABLES = /^--tw-(?:translate-[xyz]|rotate(?:-[xyz])?|skew-[xy]|scale-[xyz])$/;
3536
+ const lowerTransformVariables = (root)=>{
3537
+ root.walkDecls(TRANSFORM_VARIABLES, (declaration)=>{
3538
+ declaration.value = transformValue(declaration.prop, declaration.value);
3539
+ });
3540
+ };
3541
+ const lowerTransformProperty = (declaration)=>{
3542
+ const { prop } = declaration;
3543
+ const values = splitSpaces(declaration.value);
3544
+ switch(prop){
3545
+ case 'translate':
3546
+ if (!isTailwindComposition(values, '--tw-translate-')) {
3547
+ const [x, y = '0', z = '0'] = 'none' === values[0] ? [
3548
+ '0',
3549
+ '0',
3550
+ '0'
3551
+ ] : values;
3552
+ setVariables(declaration, {
3553
+ '--tw-translate-x': x,
3554
+ '--tw-translate-y': y,
3555
+ '--tw-translate-z': z
3556
+ });
3557
+ }
3558
+ break;
3559
+ case 'rotate':
3560
+ if (1 !== values.length) return false;
3561
+ setVariables(declaration, {
3562
+ '--tw-rotate': 'none' === values[0] ? '0deg' : values[0]
3563
+ });
3564
+ break;
3565
+ case 'scale':
3566
+ if (!isTailwindComposition(values, '--tw-scale-')) {
3567
+ const [x, y = x] = 'none' === values[0] ? [
3568
+ '1',
3569
+ '1'
3570
+ ] : values;
3571
+ setVariables(declaration, {
3572
+ '--tw-scale-x': x,
3573
+ '--tw-scale-y': y
3574
+ });
3575
+ }
3576
+ break;
3577
+ case 'transform':
3578
+ if (!isTailwindComposition(values, '--tw-')) return false;
3579
+ break;
3580
+ default:
3581
+ return false;
3582
+ }
3583
+ return true;
3584
+ };
3585
+ const isTailwindComposition = (values, prefix)=>values.length > 0 && values.every((value)=>value.startsWith(`var(${prefix}`));
3586
+ const setVariables = (declaration, variables)=>{
3587
+ for (const [prop, value] of Object.entries(variables))declaration.before({
3588
+ prop,
3589
+ value: transformValue(prop, value)
3590
+ });
3591
+ };
3592
+ const transformValue = (prop, value)=>{
3593
+ const folded = foldCalc(value);
3594
+ if (!prop.startsWith('--tw-scale-')) return folded;
3595
+ const unit = postcss_value_parser.unit(folded);
3596
+ return unit && '%' === unit.unit ? String(Number(unit.number) / 100) : folded;
3597
+ };
3598
+ const splitSpaces = (value)=>postcss_value_parser(value).nodes.filter((node)=>'space' !== node.type && 'comment' !== node.type).map((node)=>postcss_value_parser.stringify(node));
3599
+ const lowerValue = (declaration)=>{
3600
+ switch(declaration.prop){
3601
+ case 'opacity':
3602
+ {
3603
+ const unit = postcss_value_parser.unit(declaration.value);
3604
+ if (unit && '%' === unit.unit) declaration.value = String(Number(unit.number) / 100);
3605
+ break;
3606
+ }
3607
+ case 'text-decoration-line':
3608
+ declaration.prop = 'text-decoration';
3609
+ break;
3610
+ case 'transition-property':
3611
+ declaration.value = lowerTransitionProperty(declaration.value);
3612
+ break;
3613
+ case '--tw-gradient-position':
3614
+ declaration.value = lowerGradientPosition(declaration);
3615
+ break;
3616
+ default:
3617
+ if (/\binfinity\b/i.test(declaration.value)) declaration.value = foldCalc(declaration.value);
3618
+ declaration.value = clampHugeNumbers(declaration.value);
3619
+ break;
3620
+ }
3621
+ };
3622
+ const lowerTransitionProperty = (value)=>{
3623
+ const properties = value.split(',').map((property)=>{
3624
+ const name = property.trim().toLowerCase();
3625
+ return [
3626
+ 'translate',
3627
+ 'rotate',
3628
+ 'scale'
3629
+ ].includes(name) ? 'transform' : name;
3630
+ }).filter((name)=>'all' === name || 'none' === name || LYNX_PROPERTIES.has(name));
3631
+ return [
3632
+ ...new Set(properties)
3633
+ ].join(', ') || 'none';
3634
+ };
3635
+ const lowerGradientPosition = (declaration)=>{
3636
+ const position = declaration.value.replace(/\s*\bin\s+[a-z0-9-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/gi, '').trim();
3637
+ if (position) return position;
3638
+ let gradient = 'linear';
3639
+ declaration.parent?.walkDecls('background-image', ({ value })=>{
3640
+ gradient = /^(radial|conic)-gradient\(/.exec(value)?.[1] ?? gradient;
3641
+ });
3642
+ return ({
3643
+ linear: 'to bottom',
3644
+ radial: 'ellipse',
3645
+ conic: 'from 0deg'
3646
+ })[gradient];
3647
+ };
3648
+ const LOGICAL_PAIRS = {
3649
+ 'padding-inline': [
3650
+ 'padding-left',
3651
+ 'padding-right'
3652
+ ],
3653
+ 'padding-block': [
3654
+ 'padding-top',
3655
+ 'padding-bottom'
3656
+ ],
3657
+ 'margin-inline': [
3658
+ 'margin-left',
3659
+ 'margin-right'
3660
+ ],
3661
+ 'margin-block': [
3662
+ 'margin-top',
3663
+ 'margin-bottom'
3664
+ ],
3665
+ 'inset-inline': [
3666
+ 'left',
3667
+ 'right'
3668
+ ],
3669
+ 'inset-block': [
3670
+ 'top',
3671
+ 'bottom'
3672
+ ],
3673
+ 'border-inline-width': [
3674
+ 'border-left-width',
3675
+ 'border-right-width'
3676
+ ],
3677
+ 'border-block-width': [
3678
+ 'border-top-width',
3679
+ 'border-bottom-width'
3680
+ ],
3681
+ 'border-inline-style': [
3682
+ 'border-left-style',
3683
+ 'border-right-style'
3684
+ ],
3685
+ 'border-block-style': [
3686
+ 'border-top-style',
3687
+ 'border-bottom-style'
3688
+ ],
3689
+ 'border-inline-color': [
3690
+ 'border-left-color',
3691
+ 'border-right-color'
3692
+ ],
3693
+ 'border-block-color': [
3694
+ 'border-top-color',
3695
+ 'border-bottom-color'
3696
+ ]
3697
+ };
3698
+ const LOGICAL_BOTH_SIDES = {
3699
+ 'border-inline': [
3700
+ 'border-left',
3701
+ 'border-right'
3702
+ ],
3703
+ 'border-block': [
3704
+ 'border-top',
3705
+ 'border-bottom'
3706
+ ]
3707
+ };
3708
+ const LOGICAL_LONGHANDS = {
3709
+ 'padding-inline-start': 'padding-left',
3710
+ 'padding-inline-end': 'padding-right',
3711
+ 'padding-block-start': 'padding-top',
3712
+ 'padding-block-end': 'padding-bottom',
3713
+ 'margin-inline-start': 'margin-left',
3714
+ 'margin-inline-end': 'margin-right',
3715
+ 'margin-block-start': 'margin-top',
3716
+ 'margin-block-end': 'margin-bottom',
3717
+ 'inset-inline-start': 'left',
3718
+ 'inset-inline-end': 'right',
3719
+ 'inset-block-start': 'top',
3720
+ 'inset-block-end': 'bottom',
3721
+ 'border-inline-start': 'border-left',
3722
+ 'border-inline-end': 'border-right',
3723
+ 'border-block-start': 'border-top',
3724
+ 'border-block-end': 'border-bottom',
3725
+ 'border-inline-start-width': 'border-left-width',
3726
+ 'border-inline-end-width': 'border-right-width',
3727
+ 'border-block-start-width': 'border-top-width',
3728
+ 'border-block-end-width': 'border-bottom-width',
3729
+ 'border-inline-start-style': 'border-left-style',
3730
+ 'border-inline-end-style': 'border-right-style',
3731
+ 'border-block-start-style': 'border-top-style',
3732
+ 'border-block-end-style': 'border-bottom-style',
3733
+ 'border-inline-start-color': 'border-left-color',
3734
+ 'border-inline-end-color': 'border-right-color',
3735
+ 'border-block-start-color': 'border-top-color',
3736
+ 'border-block-end-color': 'border-bottom-color',
3737
+ 'border-start-start-radius': 'border-top-left-radius',
3738
+ 'border-start-end-radius': 'border-top-right-radius',
3739
+ 'border-end-start-radius': 'border-bottom-left-radius',
3740
+ 'border-end-end-radius': 'border-bottom-right-radius'
3741
+ };
3742
+ const expandLogical = (declaration)=>{
3743
+ const { prop, value, important } = declaration;
3744
+ const longhand = LOGICAL_LONGHANDS[prop];
3745
+ if (longhand) {
3746
+ declaration.prop = longhand;
3747
+ return;
3748
+ }
3749
+ let expanded;
3750
+ const pair = LOGICAL_PAIRS[prop];
3751
+ const bothSides = LOGICAL_BOTH_SIDES[prop];
3752
+ if (pair) {
3753
+ const [start, end = start] = splitSpaces(value);
3754
+ expanded = [
3755
+ [
3756
+ pair[0],
3757
+ start
3758
+ ],
3759
+ [
3760
+ pair[1],
3761
+ end
3762
+ ]
3763
+ ];
3764
+ } else if (bothSides) expanded = [
3765
+ [
3766
+ bothSides[0],
3767
+ value
3768
+ ],
3769
+ [
3770
+ bothSides[1],
3771
+ value
3772
+ ]
3773
+ ];
3774
+ else if ('inset' === prop) {
3775
+ const [top, right = top, bottom = top, left = right] = splitSpaces(value);
3776
+ expanded = [
3777
+ [
3778
+ 'top',
3779
+ top
3780
+ ],
3781
+ [
3782
+ 'right',
3783
+ right
3784
+ ],
3785
+ [
3786
+ 'bottom',
3787
+ bottom
3788
+ ],
3789
+ [
3790
+ 'left',
3791
+ left
3792
+ ]
3793
+ ];
3794
+ }
3795
+ if (!expanded) return;
3796
+ for (const [physical, physicalValue] of expanded)declaration.before({
3797
+ prop: physical,
3798
+ value: physicalValue,
3799
+ important
3800
+ });
3801
+ declaration.remove();
3802
+ };
3803
+ const dropUnsupportedProperties = (rule)=>{
3804
+ rule.each((node)=>{
3805
+ if ('decl' !== node.type || node.prop.startsWith('--')) return;
3806
+ const prop = node.prop.toLowerCase();
3807
+ const keywords = LYNX_KEYWORDS.get(prop);
3808
+ const value = node.value.trim().toLowerCase();
3809
+ if (!LYNX_PROPERTIES.has(prop) || void 0 !== keywords && /^[a-z-]+$/.test(value) && !GLOBAL_KEYWORDS.has(value) && !keywords.has(value)) node.remove();
3810
+ });
3811
+ };
3812
+ const GLOBAL_KEYWORDS = new Set([
3813
+ 'inherit',
3814
+ 'initial',
3815
+ 'unset',
3816
+ 'revert'
3817
+ ]);
3818
+ const lowerSelectors = (rule)=>{
3819
+ const selectors = rule.selectors.filter((selector)=>!/(?<!\\):host\b/.test(selector)).flatMap(unwrapWhereAndIs);
3820
+ if (0 === selectors.length) return false;
3821
+ rule.selectors = selectors.every((selector)=>NOT_LAST_CHILD.test(selector)) && mirrorBetweenChildren(rule) ? selectors.map((selector)=>selector.replace(NOT_LAST_CHILD, ' > * + *')) : selectors;
3822
+ return true;
3823
+ };
3824
+ const unwrapWhereAndIs = (selector)=>{
3825
+ const match = /^:(?:where|is)\(/i.exec(selector);
3826
+ if (!match || closingParenthesis(selector, match[0].length) !== selector.length - 1) return [
3827
+ selector
3828
+ ];
3829
+ return splitTopLevel(selector.slice(match[0].length, -1)).flatMap(unwrapWhereAndIs);
3830
+ };
3831
+ const closingParenthesis = (text, start)=>{
3832
+ let depth = 1;
3833
+ for(let index = start; index < text.length; index++){
3834
+ const character = text[index];
3835
+ if ('\\' === character) index++;
3836
+ else if ('(' === character) depth++;
3837
+ else if (')' === character && 0 === --depth) return index;
3838
+ }
3839
+ return -1;
3840
+ };
3841
+ const splitTopLevel = (list)=>{
3842
+ const parts = [];
3843
+ let depth = 0;
3844
+ let current = '';
3845
+ for(let index = 0; index < list.length; index++){
3846
+ const character = list[index];
3847
+ if ('\\' === character) {
3848
+ current += list.slice(index, index + 2);
3849
+ index++;
3850
+ continue;
3851
+ }
3852
+ if ('(' === character) depth++;
3853
+ else if (')' === character) depth--;
3854
+ else if (',' === character && 0 === depth) {
3855
+ parts.push(current.trim());
3856
+ current = '';
3857
+ continue;
3858
+ }
3859
+ current += character;
3860
+ }
3861
+ parts.push(current.trim());
3862
+ return parts;
3863
+ };
3864
+ const NOT_LAST_CHILD = /\s*>\s*:not\(:last-child\)$/;
3865
+ const OPPOSITE_SIDES = {
3866
+ top: 'bottom',
3867
+ bottom: 'top',
3868
+ left: 'right',
3869
+ right: 'left',
3870
+ 'block-start': 'block-end',
3871
+ 'block-end': 'block-start',
3872
+ 'inline-start': 'inline-end',
3873
+ 'inline-end': 'inline-start'
3874
+ };
3875
+ const mirrorBetweenChildren = (rule)=>{
3876
+ const sidesOnly = rule.nodes.every((node)=>'decl' !== node.type || node.prop.startsWith('--') || /^(margin|padding|border)-/.test(node.prop));
3877
+ if (!sidesOnly) return false;
3878
+ rule.walkDecls((declaration)=>{
3879
+ if (declaration.prop.startsWith('--')) return;
3880
+ declaration.prop = declaration.prop.replace(/\b(top|bottom|left|right|block-start|block-end|inline-start|inline-end)\b/, (side)=>OPPOSITE_SIDES[side]);
3881
+ });
3882
+ return true;
3883
+ };
3884
+ const flattenNesting = (root)=>{
3885
+ root.each((node)=>{
3886
+ flattenNode(node);
3887
+ });
3888
+ };
3889
+ const flattenNode = (node)=>{
3890
+ if ('rule' === node.type) flattenRule(node);
3891
+ else if ('atrule' === node.type && node.nodes) node.each((child)=>{
3892
+ flattenNode(child);
3893
+ });
3894
+ };
3895
+ const flattenRule = (rule)=>{
3896
+ let last = rule;
3897
+ for (const child of rule.nodes.slice())if ('rule' === child.type) {
3898
+ child.selectors = resolveNested(child.selectors, rule.selectors);
3899
+ last.after(child);
3900
+ last = flattenRule(child);
3901
+ } else if ('atrule' === child.type) {
3902
+ wrapDeclarations(child, rule);
3903
+ last.after(child);
3904
+ last = child;
3905
+ flattenNode(child);
3906
+ }
3907
+ return last;
3908
+ };
3909
+ const wrapDeclarations = (atRule, rule)=>{
3910
+ if (!atRule.nodes) return;
3911
+ for (const child of atRule.nodes)if ('rule' === child.type) child.selectors = resolveNested(child.selectors, rule.selectors);
3912
+ else if ('atrule' === child.type) wrapDeclarations(child, rule);
3913
+ const declarations = atRule.nodes.filter((node)=>'decl' === node.type);
3914
+ if (declarations.length > 0) {
3915
+ const wrapper = rule.clone();
3916
+ wrapper.removeAll();
3917
+ wrapper.append(declarations);
3918
+ atRule.prepend(wrapper);
3919
+ }
3920
+ };
3921
+ const resolveNested = (selectors, parents)=>parents.flatMap((parent)=>selectors.map((selector)=>/(?<!\\)&/.test(selector) ? selector.replaceAll(/(?<!\\)&/g, parent) : `${parent} ${selector}`));
3922
+ const unwrapLayers = (container)=>{
3923
+ container.each((node)=>{
3924
+ if ('atrule' !== node.type) return;
3925
+ if ('layer' === node.name.toLowerCase()) if (node.nodes) {
3926
+ unwrapLayers(node);
3927
+ node.replaceWith(node.nodes);
3928
+ } else node.remove();
3929
+ else if (node.nodes) unwrapLayers(node);
3930
+ });
3931
+ };
3932
+ const takePropertyRegistrations = (root)=>{
3933
+ const initialValues = new Map();
3934
+ root.walkAtRules(/^property$/i, (atRule)=>{
3935
+ atRule.walkDecls('initial-value', (declaration)=>{
3936
+ initialValues.set(atRule.params.trim(), declaration.value);
3937
+ });
3938
+ atRule.remove();
3939
+ });
3940
+ root.walkAtRules(/^supports$/i, (atRule)=>{
3941
+ const rules = atRule.nodes?.filter((node)=>'rule' === node.type) ?? [];
3942
+ if (rules.length > 0 && rules.every((rule)=>rule.selector.includes('::backdrop'))) atRule.remove();
3943
+ });
3944
+ return initialValues;
3945
+ };
3946
+ const removeEmptyContainers = (container)=>{
3947
+ container.each((node)=>{
3948
+ if ('rule' !== node.type && 'atrule' !== node.type) return;
3949
+ if (!node.nodes) return;
3950
+ removeEmptyContainers(node);
3951
+ if (node.nodes.every((child)=>'comment' === child.type)) node.remove();
3952
+ });
3953
+ };
3954
+ const IMPLIED_KEYFRAME_VALUES = {
3955
+ transform: 'none',
3956
+ opacity: '1'
3957
+ };
3958
+ const completeKeyframes = (root)=>{
3959
+ root.walkAtRules(/^(?:-webkit-)?keyframes$/i, (atRule)=>{
3960
+ const offsets = new Set();
3961
+ const properties = new Set();
3962
+ atRule.walkRules((rule)=>{
3963
+ for (const selector of rule.selectors)offsets.add({
3964
+ from: '0%',
3965
+ to: '100%'
3966
+ }[selector.toLowerCase()] ?? selector);
3967
+ rule.walkDecls(({ prop })=>{
3968
+ if (prop in IMPLIED_KEYFRAME_VALUES) properties.add(prop);
3969
+ });
3970
+ });
3971
+ const first = atRule.first;
3972
+ if (0 === properties.size || first?.type !== 'rule') return;
3973
+ for (const [offset, selector] of [
3974
+ [
3975
+ '0%',
3976
+ 'from'
3977
+ ],
3978
+ [
3979
+ '100%',
3980
+ 'to'
3981
+ ]
3982
+ ]){
3983
+ if (offsets.has(offset)) continue;
3984
+ const keyframe = first.clone({
3985
+ selector
3986
+ });
3987
+ keyframe.removeAll();
3988
+ for (const prop of properties)keyframe.append({
3989
+ prop,
3990
+ value: IMPLIED_KEYFRAME_VALUES[prop]
3991
+ });
3992
+ if ('from' === selector) atRule.prepend(keyframe);
3993
+ else atRule.append(keyframe);
3994
+ }
3995
+ });
3996
+ };
3997
+ const resolveNestedVariables = (root, registrations)=>{
3998
+ const theme = themeVariables(root);
3999
+ root.walkDecls(/^--/, (declaration)=>{
4000
+ if (!declaration.value.includes('var(')) return;
4001
+ const parsed = postcss_value_parser(declaration.value);
4002
+ const lookup = (name)=>sameRuleValue(declaration, name) ?? theme.get(name) ?? registrations.get(name);
4003
+ if (resolveInFunctions(parsed.nodes, false, lookup, new Set())) declaration.value = foldCalc(parsed.toString());
4004
+ });
4005
+ };
4006
+ const themeVariables = (root)=>{
4007
+ const variables = new Map();
4008
+ root.walkRules((rule)=>{
4009
+ if (rule.selectors.includes(':root')) rule.walkDecls(/^--/, ({ prop, value })=>{
4010
+ variables.set(prop, value);
4011
+ });
4012
+ });
4013
+ return variables;
4014
+ };
4015
+ const sameRuleValue = (declaration, name)=>{
4016
+ let value;
4017
+ declaration.parent.each((node)=>{
4018
+ if ('decl' === node.type && node.prop === name && node !== declaration) value = node.value;
4019
+ });
4020
+ return value;
4021
+ };
4022
+ const resolveInFunctions = (nodes, insideFunction, lookup, seen)=>{
4023
+ let changed = false;
4024
+ for (const node of nodes){
4025
+ if ('function' !== node.type) continue;
4026
+ if ('var' !== node.value.toLowerCase()) {
4027
+ changed = resolveInFunctions(node.nodes, true, lookup, seen) || changed;
4028
+ continue;
4029
+ }
4030
+ const resolved = insideFunction ? resolveVariable(node, lookup, seen) : void 0;
4031
+ if (void 0 === resolved) {
4032
+ changed = resolveInFunctions(node.nodes, insideFunction, lookup, seen) || changed;
4033
+ continue;
4034
+ }
4035
+ Object.assign(node, {
4036
+ type: 'word',
4037
+ value: resolved
4038
+ });
4039
+ changed = true;
4040
+ }
4041
+ return changed;
4042
+ };
4043
+ const resolveVariable = (node, lookup, seen)=>{
4044
+ const name = node.nodes[0]?.value ?? '';
4045
+ if (seen.has(name)) return;
4046
+ const commaIndex = node.nodes.findIndex((child)=>'div' === child.type && ',' === child.value);
4047
+ const fallback = -1 === commaIndex ? void 0 : postcss_value_parser.stringify(node.nodes.slice(commaIndex + 1)).trim();
4048
+ const value = lookup(name) ?? fallback;
4049
+ if (void 0 === value) return;
4050
+ const parsed = postcss_value_parser(value);
4051
+ resolveInFunctions(parsed.nodes, true, lookup, new Set([
4052
+ ...seen,
4053
+ name
4054
+ ]));
4055
+ return parsed.toString();
4056
+ };
4057
+ const TAILWIND_POSTCSS = '@tailwindcss/postcss';
4058
+ const tailwindForLynx = ()=>({
4059
+ postcssPlugin: 'angular-lynx-tailwind',
4060
+ OnceExit (root, { result, rule }) {
4061
+ if (!compiledByTailwind(result)) return;
4062
+ lowerForLynx(root, (selector)=>rule({
4063
+ selector
4064
+ }));
4065
+ }
4066
+ });
4067
+ tailwindForLynx.postcss = true;
4068
+ const compiledByTailwind = (result)=>result.messages.some((message)=>message.plugin === TAILWIND_POSTCSS);
4069
+ const lowerForLynx = (root, createRule)=>{
4070
+ flattenNesting(root);
4071
+ unwrapLayers(root);
4072
+ const registrations = takePropertyRegistrations(root);
4073
+ const defaults = new Map(registrations);
4074
+ const channelVariables = colorChannelVariables(root);
4075
+ root.walkRules((rule)=>{
4076
+ const inKeyframes = rule.parent?.type === 'atrule' && /keyframes$/i.test(rule.parent.name);
4077
+ if (!inKeyframes && !lowerSelectors(rule)) return void rule.remove();
4078
+ if (lowerDeclarations(rule, channelVariables).composesTransform) {
4079
+ for (const [name, value] of TRANSFORM_DEFAULTS)if (!defaults.has(name)) defaults.set(name, value);
4080
+ }
4081
+ });
4082
+ unwrapLoweredColorMixSupports(root);
4083
+ resolveNestedVariables(root, registrations);
4084
+ lowerTransformVariables(root);
4085
+ completeKeyframes(root);
4086
+ removeEmptyContainers(root);
4087
+ if (defaults.size > 0) {
4088
+ const defaultsRule = createRule('*');
4089
+ for (const [prop, value] of defaults)defaultsRule.append({
4090
+ prop,
4091
+ value
4092
+ });
4093
+ const imports = root.nodes.filter((node)=>'atrule' === node.type && /^(charset|import)$/i.test(node.name));
4094
+ if (imports.length > 0) imports[imports.length - 1].after(defaultsRule);
4095
+ else root.prepend(defaultsRule);
4096
+ }
4097
+ };
4098
+ const colorChannelVariables = (root)=>{
4099
+ const names = new Set();
4100
+ root.walkDecls(/^--.+-rgb$/, ({ prop })=>{
4101
+ names.add(prop.slice(0, -4));
4102
+ });
4103
+ return names;
4104
+ };
4105
+ const unwrapLoweredColorMixSupports = (root)=>{
4106
+ root.walkAtRules(/^supports$/i, (atRule)=>{
4107
+ if (!atRule.params.includes('color-mix(') || !atRule.nodes) return;
4108
+ let mixes = false;
4109
+ atRule.walkDecls((declaration)=>{
4110
+ mixes ||= declaration.value.includes('color-mix(');
4111
+ });
4112
+ if (!mixes) atRule.replaceWith(atRule.nodes);
4113
+ });
4114
+ };
1757
4115
  const TAILWIND_CONFIG_FILES = [
1758
4116
  'tailwind.config.ts',
1759
4117
  'tailwind.config.js',
1760
4118
  'tailwind.config.mjs',
1761
4119
  'tailwind.config.cjs'
1762
4120
  ];
4121
+ const isTailwind = (plugin)=>'function' == typeof plugin ? 'tailwindcss' === plugin.name : plugin?.postcssPlugin === 'tailwindcss';
4122
+ const isTailwind4 = (plugin)=>plugin?.postcssPlugin === '@tailwindcss/postcss';
1763
4123
  const applyTailwind = (api)=>{
1764
4124
  const configFile = findUp(TAILWIND_CONFIG_FILES, process.cwd());
1765
- if (!configFile) return;
1766
- pluginTailwindCSS({
1767
- config: configFile,
1768
- exclude: [
1769
- /[\\/]node_modules[\\/]/
1770
- ]
1771
- }).setup(api);
4125
+ let warned = false;
4126
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig })=>mergeRsbuildConfig(config, {
4127
+ tools: {
4128
+ postcss: ({ postcssOptions }, { addPlugins })=>{
4129
+ const configured = 'object' == typeof postcssOptions ? postcssOptions.plugins ?? [] : [];
4130
+ if (configFile && !configured.some(isTailwind)) {
4131
+ const require = createRequire(configFile);
4132
+ const { version } = require('tailwindcss/package.json');
4133
+ if (Number.parseInt(version, 10) < 4) {
4134
+ const tailwindcss = require('tailwindcss');
4135
+ addPlugins(tailwindcss({
4136
+ config: configFile
4137
+ }));
4138
+ } else if (!configured.some(isTailwind4) && !warned) {
4139
+ warned = true;
4140
+ api.logger.warn(`Found ${configFile}, but Tailwind CSS ${version} is installed, which doesn't run from a Tailwind config file. List @tailwindcss/postcss in a .postcssrc.json, or run \`ng generate @blotch/angular-lynx:add-tailwind\`.`);
4141
+ }
4142
+ }
4143
+ addPlugins(tailwindForLynx());
4144
+ }
4145
+ }
4146
+ }));
1772
4147
  };
4148
+ const DEPRECATED_OPTIONS = [
4149
+ 'enableICU',
4150
+ 'enableParallelElement',
4151
+ 'pipelineSchedulerConfig'
4152
+ ];
4153
+ const deprecatedOptionsIn = (options)=>DEPRECATED_OPTIONS.filter((name)=>options?.[name] !== void 0);
1773
4154
  const normalizeOptions = (options)=>{
1774
4155
  const defaultOptions = {
1775
4156
  customCSSInheritanceList: void 0,
@@ -1779,20 +4160,19 @@ const normalizeOptions = (options)=>{
1779
4160
  enableCSSInheritance: false,
1780
4161
  enableCSSInvalidation: false,
1781
4162
  enableCSSSelector: true,
1782
- enableICU: false,
1783
4163
  enableNewGesture: true,
1784
- enableParallelElement: true,
1785
4164
  defaultDisplayLinear: true,
1786
4165
  enableRemoveCSSScope: false,
1787
4166
  enableSSR: false,
1788
4167
  pages: void 0,
1789
- pipelineSchedulerConfig: 0x00010000,
1790
4168
  targetSdkVersion: '3.2',
1791
4169
  defaultOverflowVisible: true,
1792
4170
  removeDescendantSelectorScope: false,
1793
4171
  experimental_isLazyBundle: false
1794
4172
  };
1795
- return Object.assign(defaultOptions, options);
4173
+ const normalized = Object.assign(defaultOptions, options);
4174
+ for (const name of DEPRECATED_OPTIONS)delete normalized[name];
4175
+ return normalized;
1796
4176
  };
1797
4177
  const pluginAngularLynx = (options)=>({
1798
4178
  name: 'lynx:angular',
@@ -1801,12 +4181,18 @@ const pluginAngularLynx = (options)=>({
1801
4181
  ANGULAR_CLI_CONTEXT_PLUGIN_NAME
1802
4182
  ],
1803
4183
  setup: async (api)=>{
4184
+ for (const name of deprecatedOptionsIn(options))api.logger.warn(`pluginAngularLynx(): the \`${name}\` option has no effect and will be removed in a future release. Remove it from your config.`);
1804
4185
  const normalizedOptions = normalizeOptions(options);
4186
+ const backgroundEntries = createBackgroundEntries({
4187
+ rootPath: api.context.rootPath
4188
+ });
1805
4189
  applyCSS(api, normalizedOptions);
1806
4190
  applyTailwind(api);
1807
- applyEntry(api, normalizedOptions);
4191
+ const appEntries = new Set();
4192
+ applyEntry(api, normalizedOptions, backgroundEntries, appEntries);
1808
4193
  applyLayers(api);
1809
- const angularRules = applyAngularRules(api, normalizedOptions);
4194
+ applyResolve(api);
4195
+ const angularRules = applyAngularRules(api, normalizedOptions, backgroundEntries, appEntries);
1810
4196
  applyGenerator(api);
1811
4197
  applySplitChunksRule(api, normalizedOptions);
1812
4198
  applyDevLogger(api);