ngx-theme-stack 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,5 +13,6 @@ export declare const DEFAULTS: {
13
13
  readonly defaultTheme: "system";
14
14
  readonly storageKey: "ngx-theme-stack-theme";
15
15
  readonly mode: "class";
16
+ readonly strategy: "critters";
16
17
  readonly themes: readonly ["system", "light", "dark"];
17
18
  };
@@ -16,6 +16,7 @@ exports.DEFAULTS = {
16
16
  defaultTheme: 'system',
17
17
  storageKey: 'ngx-theme-stack-theme',
18
18
  mode: 'class',
19
+ strategy: 'critters',
19
20
  themes: [...exports.DEFAULT_THEMES],
20
21
  };
21
22
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/constants.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;GASG;AACU,QAAA,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAEtD,QAAA,QAAQ,GAAG;IACtB,YAAY,EAAE,QAAQ;IACtB,UAAU,EAAE,uBAAuB;IACnC,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,CAAC,GAAG,sBAAc,CAAC;CACnB,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/constants.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;GASG;AACU,QAAA,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAEtD,QAAA,QAAQ,GAAG;IACtB,YAAY,EAAE,QAAQ;IACtB,UAAU,EAAE,uBAAuB;IACnC,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,CAAC,GAAG,sBAAc,CAAC;CACnB,CAAC"}
@@ -40,8 +40,14 @@ function collectCustomOptions() {
40
40
  const storageKey = rawKey || constants_1.DEFAULTS.storageKey;
41
41
  const MODES = ['class', 'attribute', 'both'];
42
42
  const mode = yield (0, utils_1.askList)(rl, 'How to apply theme on <html>:', MODES, 0);
43
+ const STRATEGIES = ['critters', 'blocking'];
43
44
  process.stdout.write('\n');
44
- return { defaultTheme, storageKey, mode, themes: allThemes };
45
+ process.stdout.write(' Anti-flash strategy:\n');
46
+ process.stdout.write(' - critters: Zero network requests (inlines CSS in <head>)\n');
47
+ process.stdout.write(' - blocking: Standard CSS loading (themes.css)\n');
48
+ const strategy = (yield (0, utils_1.askList)(rl, 'Choose strategy:', STRATEGIES, 0));
49
+ process.stdout.write('\n');
50
+ return { defaultTheme, storageKey, mode, themes: allThemes, strategy };
45
51
  }
46
52
  finally {
47
53
  rl.close();
@@ -77,33 +83,104 @@ function ngAdd(options) {
77
83
  context.logger.info('');
78
84
  let provideCall;
79
85
  let scriptOptions;
86
+ let finalThemes;
87
+ let themesToScaffold;
88
+ let finalStrategy;
80
89
  if (options.mode === 'quick') {
81
- provideCall = 'provideThemeStack()';
82
- scriptOptions = {
83
- storageKey: constants_1.DEFAULTS.storageKey,
84
- defaultTheme: constants_1.DEFAULTS.defaultTheme,
85
- mode: constants_1.DEFAULTS.mode,
86
- };
87
- context.logger.info('⚡ Quick setup defaults applied by the library (DEFAULT_NG_CONFIG).');
90
+ const themes = [...constants_1.DEFAULT_THEMES];
91
+ const { defaultTheme, storageKey, mode } = constants_1.DEFAULTS;
92
+ const strategy = 'critters'; // default for quick
93
+ provideCall = (0, utils_1.buildProvideCall)(defaultTheme, storageKey, mode, themes);
94
+ scriptOptions = { storageKey, defaultTheme, mode };
95
+ finalThemes = themes;
96
+ themesToScaffold = themes.filter((t) => t !== 'system');
97
+ finalStrategy = strategy;
98
+ context.logger.info('⚡ Quick setup — providing explicit defaults (strategy: critters).');
88
99
  }
89
100
  else {
90
101
  context.logger.info('🛠 Custom setup — answer the prompts below:');
91
102
  const opts = yield collectCustomOptions();
92
- const { defaultTheme, storageKey, mode, themes } = opts;
103
+ const { defaultTheme, storageKey, mode, themes, strategy } = opts;
93
104
  context.logger.info(' Applying your configuration:');
94
105
  context.logger.info(` defaultTheme : ${defaultTheme}`);
95
106
  context.logger.info(` themes : [${themes.join(', ')}]`);
96
107
  context.logger.info(` storageKey : ${storageKey}`);
97
108
  context.logger.info(` mode : ${mode}`);
109
+ context.logger.info(` strategy : ${strategy}`);
98
110
  provideCall = (0, utils_1.buildProvideCall)(defaultTheme, storageKey, mode, themes);
99
111
  scriptOptions = { storageKey, defaultTheme, mode };
112
+ finalThemes = themes;
113
+ themesToScaffold = themes.filter((t) => t !== 'system');
114
+ finalStrategy = strategy;
100
115
  }
101
116
  return (0, schematics_1.chain)([
117
+ (t, context) => {
118
+ const themesPath = `${projectSourceRoot}/themes.css`;
119
+ if (!t.exists(themesPath)) {
120
+ let content = '/* ngx-theme-stack tokens */\n\n';
121
+ themesToScaffold.forEach((theme) => {
122
+ const selector = scriptOptions.mode === 'attribute' ? `[data-theme="${theme}"]` : `.${theme}`;
123
+ if (theme === 'light') {
124
+ content += `:root, ${selector} {\n /* Add your light theme variables here */\n}\n\n`;
125
+ }
126
+ else {
127
+ content += `${selector} {\n /* Add your ${theme} theme variables here */\n}\n\n`;
128
+ }
129
+ });
130
+ t.create(themesPath, content);
131
+ context.logger.info(`\n \u001b[36mResume :\u001b[0m \n`);
132
+ context.logger.info(`\u001b[32m✔ Created ${themesPath} with your theme selectors.\u001b[0m`);
133
+ }
134
+ else {
135
+ context.logger.info(`\n \u001b[36mResume :\u001b[0m \n`);
136
+ context.logger.info(`\u001b[33mℹ ${themesPath} already exists. Skipping creation to preserve your styles.\u001b[0m`);
137
+ context.logger.info(` Tip: Make sure to manually add selectors (class or attribute) for any new themes.`);
138
+ }
139
+ },
140
+ (t) => {
141
+ const pkgPath = '/package.json';
142
+ const buffer = t.read(pkgPath);
143
+ if (buffer) {
144
+ const pkg = JSON.parse(buffer.toString());
145
+ pkg.scripts = pkg.scripts || {};
146
+ pkg.scripts.prebuild = `ng generate ngx-theme-stack:sync --project ${projectName}`;
147
+ t.overwrite(pkgPath, JSON.stringify(pkg, null, 2));
148
+ }
149
+ },
150
+ (t, context) => {
151
+ var _a;
152
+ const workspaceConfig = t.read('/angular.json');
153
+ if (workspaceConfig) {
154
+ const workspace = JSON.parse(workspaceConfig.toString());
155
+ const project = workspace.projects[projectName];
156
+ const target = project.architect.build.options;
157
+ const themesPath = `${projectSourceRoot}/themes.css`.replace(/^\//, '');
158
+ // Add themes.css to styles if not already there
159
+ if (target.styles && !target.styles.includes(themesPath)) {
160
+ target.styles.unshift(themesPath);
161
+ }
162
+ // Handle inlineCritical optimization for the blocking strategy
163
+ const prodConfig = (_a = project.architect.build.configurations) === null || _a === void 0 ? void 0 : _a.production;
164
+ if (prodConfig && finalStrategy === 'blocking') {
165
+ if (typeof prodConfig.optimization === 'object') {
166
+ prodConfig.optimization.styles = prodConfig.optimization.styles || {};
167
+ prodConfig.optimization.styles.inlineCritical = false;
168
+ }
169
+ else {
170
+ prodConfig.optimization = {
171
+ styles: { inlineCritical: false },
172
+ };
173
+ }
174
+ context.logger.info(`✔ Disabled inlineCritical in angular.json for blocking strategy.`);
175
+ }
176
+ t.overwrite('/angular.json', JSON.stringify(workspace, null, 2));
177
+ }
178
+ },
102
179
  (t, ctx) => {
103
180
  (0, app_config_1.patchAppConfig)(t, ctx, projectSourceRoot, provideCall);
104
- (0, anti_flash_1.patchIndexHtml)(t, ctx, projectSourceRoot, scriptOptions);
181
+ (0, anti_flash_1.patchIndexHtml)(t, ctx, projectSourceRoot, Object.assign(Object.assign({}, scriptOptions), { themes: finalThemes, strategy: options.strategy || finalStrategy }));
105
182
  ctx.logger.info('');
106
- ctx.logger.info('✅ Done! Run `ng serve` to see ngx-theme-stack in action.');
183
+ ctx.logger.info('✅ Done! ngx-theme-stack is ready with automatic sync on build.');
107
184
  ctx.logger.info('');
108
185
  },
109
186
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,6 @@ export interface Schema {
3
3
  project: string;
4
4
  /** 'quick' applies defaults silently. 'custom' prompts interactively. */
5
5
  mode: 'quick' | 'custom';
6
+ /** The strategy to prevent theme flicker. */
7
+ strategy: 'critters' | 'blocking';
6
8
  }
@@ -22,14 +22,19 @@
22
22
  "items": [
23
23
  {
24
24
  "value": "quick",
25
- "label": "Quick – apply defaults instantly (theme: system, mode: class)"
25
+ "label": "Quick – apply defaults instantly (theme: system, mode: class, strategy: critters)"
26
26
  },
27
27
  {
28
28
  "value": "custom",
29
- "label": "Custom – choose themes, default theme, storage key and apply mode"
29
+ "label": "Custom – choose themes, default theme, storage key and strategy"
30
30
  }
31
31
  ]
32
32
  }
33
+ },
34
+ "strategy": {
35
+ "type": "string",
36
+ "description": "The strategy to use for flash prevention (critters or blocking).",
37
+ "enum": ["blocking", "critters"]
33
38
  }
34
39
  },
35
40
  "required": ["project"]
@@ -12,9 +12,4 @@ export declare function ask(rl: readline.Interface, question: string): Promise<s
12
12
  * If the input is invalid or ignored, the default index item is returned.
13
13
  */
14
14
  export declare function askList(rl: readline.Interface, label: string, items: readonly string[], defaultIndex?: number): Promise<string>;
15
- /**
16
- * Builds the 'provideThemeStack({...})' string representation.
17
- * It compares the selected values with library defaults to generate a
18
- * minimal call string (omitting properties that match defaults).
19
- */
20
15
  export declare function buildProvideCall(defaultTheme: string, storageKey: string, mode: string, themes: string[]): string;
@@ -14,7 +14,6 @@ exports.ask = ask;
14
14
  exports.askList = askList;
15
15
  exports.buildProvideCall = buildProvideCall;
16
16
  const readline = require("readline");
17
- const constants_1 = require("./constants");
18
17
  /**
19
18
  * Creates a readline interface using the system's standard input and output.
20
19
  */
@@ -40,31 +39,15 @@ function askList(rl_1, label_1, items_1) {
40
39
  return isNaN(n) || n < 1 || n > items.length ? items[defaultIndex] : items[n - 1];
41
40
  });
42
41
  }
43
- /**
44
- * Builds the 'provideThemeStack({...})' string representation.
45
- * It compares the selected values with library defaults to generate a
46
- * minimal call string (omitting properties that match defaults).
47
- */
48
42
  function buildProvideCall(defaultTheme, storageKey, mode, themes) {
49
- const defaultThemesList = [...constants_1.DEFAULT_THEMES];
50
- const isDefaultThemes = themes.length === defaultThemesList.length && themes.every((t, i) => t === defaultThemesList[i]);
51
- const isAllDefault = defaultTheme === constants_1.DEFAULTS.defaultTheme &&
52
- storageKey === constants_1.DEFAULTS.storageKey &&
53
- mode === constants_1.DEFAULTS.mode &&
54
- isDefaultThemes;
55
- if (isAllDefault)
56
- return `provideThemeStack()`;
57
- const parts = [];
58
- if (defaultTheme !== constants_1.DEFAULTS.defaultTheme)
59
- parts.push(`defaultTheme: '${defaultTheme}'`);
60
- if (storageKey !== constants_1.DEFAULTS.storageKey)
61
- parts.push(`storageKey: '${storageKey}'`);
62
- if (mode !== constants_1.DEFAULTS.mode)
63
- parts.push(`mode: '${mode}'`);
64
- if (!isDefaultThemes) {
65
- const arr = themes.map((t) => `'${t}'`).join(', ');
66
- parts.push(`themes: [${arr}]`);
67
- }
68
- return `provideThemeStack({ ${parts.join(', ')} })`;
43
+ const themesArr = themes.map((t) => `'${t}'`).join(', ');
44
+ return [
45
+ 'provideThemeStack({',
46
+ ` themes: [${themesArr}],`,
47
+ ` defaultTheme: '${defaultTheme}',`,
48
+ ` storageKey: '${storageKey}',`,
49
+ ` mode: '${mode}',`,
50
+ ' })',
51
+ ].join('\n');
69
52
  }
70
53
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/utils.ts"],"names":[],"mappings":";;;;;;;;;;;AAMA,4BAEC;AAKD,kBAEC;AAMD,0BAWC;AAED,4CAeC;AAjDD,qCAAqC;AAGrC;;GAEG;AACH,SAAgB,QAAQ;IACtB,OAAO,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACpF,CAAC;AAED;;GAEG;AACH,SAAgB,GAAG,CAAC,EAAsB,EAAE,QAAgB;IAC1D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,SAAsB,OAAO;yDAC3B,EAAsB,EACtB,KAAa,EACb,KAAwB,EACxB,YAAY,GAAG,CAAC;QAEhB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,EAAE,EAAE,aAAa,YAAY,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpF,CAAC;CAAA;AAED,SAAgB,gBAAgB,CAC9B,YAAoB,EACpB,UAAkB,EAClB,IAAY,EACZ,MAAgB;IAEhB,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO;QACL,qBAAqB;QACrB,kBAAkB,SAAS,IAAI;QAC/B,wBAAwB,YAAY,IAAI;QACxC,sBAAsB,UAAU,IAAI;QACpC,gBAAgB,IAAI,IAAI;QACxB,QAAQ;KACT,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -1,12 +1,3 @@
1
1
  import { Rule } from '@angular-devkit/schematics';
2
2
  import { Schema } from './schema';
3
- /**
4
- * `ng generate ngx-theme-stack:sync`
5
- *
6
- * Reads the current `provideThemeStack()` configuration from `app.config.ts`
7
- * and regenerates the anti-flash inline script in `index.html` to match.
8
- *
9
- * Run this whenever you change `mode`, `storageKey`, or `defaultTheme`
10
- * inside `provideThemeStack()` to keep the anti-flash script in sync.
11
- */
12
3
  export declare function sync(options: Schema): Rule;
@@ -5,36 +5,36 @@ const constants_1 = require("../ng-add/constants");
5
5
  // ── Regex patterns ────────────────────────────────────────────────────────────
6
6
  /**
7
7
  * Matches the provideThemeStack() call in app.config.ts.
8
- * Captures the full options object string (may be empty).
8
+ * Captures the full options object string (may be empty or span multiple lines).
9
9
  *
10
10
  * Examples matched:
11
11
  * provideThemeStack()
12
12
  * provideThemeStack({ mode: 'attribute', themes: ['dark', 'light'] })
13
+ * provideThemeStack({ ← multi-line call (new explicit format)
14
+ * themes: ['light', 'dark'],
15
+ * defaultTheme: 'system',
16
+ * })
13
17
  */
14
- const PROVIDE_CALL_RE = /provideThemeStack\s*\(([^)]*)\)/;
15
- /**
16
- * Extracts "mode" value from the captured options string.
17
- * e.g. { mode: 'attribute', ... } → 'attribute'
18
- */
18
+ const PROVIDE_CALL_RE = /provideThemeStack\s*\(([\s\S]*?)\)/;
19
+ /** Extracts "mode" value from the captured options string. */
19
20
  const OPTION_MODE_RE = /mode\s*:\s*['"]([^'"]+)['"]/;
20
- /**
21
- * Extracts "storageKey" value from the captured options string.
22
- */
21
+ /** Extracts "storageKey" value from the captured options string. */
23
22
  const OPTION_KEY_RE = /storageKey\s*:\s*['"]([^'"]+)['"]/;
24
- /**
25
- * Extracts "defaultTheme" value from the captured options string.
26
- */
23
+ /** Extracts "defaultTheme" value from the captured options string. */
27
24
  const OPTION_DEFAULT_THEME_RE = /defaultTheme\s*:\s*['"]([^'"]+)['"]/;
25
+ const OPTION_STRATEGY_RE = /strategy\s*:\s*['"]([^'"]+)['"]/;
28
26
  /**
29
- * Identifies the inline anti-flash script injected by ng-add.
30
- * We look for the unique comment marker.
31
- */
32
- const SCRIPT_MARKER = 'ngx-theme-stack anti-flash';
33
- /**
34
- * Matches the complete <script> block that contains the anti-flash script.
35
- * Captures everything between the comment marker and the closing </script>.
27
+ * Extracts the themes array from the options string.
28
+ * Matches: themes: ['light', 'dark', 'sunset']
36
29
  */
30
+ const OPTION_THEMES_RE = /themes\s*:\s*\[([^\]]*)\]/;
31
+ /** Matches the complete <script> anti-flash block (identified by its marker comment). */
37
32
  const SCRIPT_BLOCK_RE = /<!-- ngx-theme-stack anti-flash -->\s*<script>[\s\S]*?<\/script>/;
33
+ /** Marker injected by ng-add that delimits the Critters Trick zone in <body>. */
34
+ const CRITTERS_MARKER = '<!-- ngx-theme-stack critters-trick -->';
35
+ /** Regex that matches the entire Critters Trick block (marker + divs). */
36
+ const CRITTERS_BLOCK_RE = /<!-- ngx-theme-stack critters-trick -->[\s\S]*?<!-- \/ngx-theme-stack critters-trick -->/;
37
+ // ── Config extraction ─────────────────────────────────────────────────────────
38
38
  /**
39
39
  * Reads `app.config.ts` (or `main.ts`) and extracts the current
40
40
  * `provideThemeStack()` configuration using regex.
@@ -42,7 +42,7 @@ const SCRIPT_BLOCK_RE = /<!-- ngx-theme-stack anti-flash -->\s*<script>[\s\S]*?<
42
42
  * Falls back to library defaults for any option that is not found.
43
43
  */
44
44
  function extractConfig(tree, sourceRoot, context) {
45
- var _a, _b, _c, _d, _e, _f;
45
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
46
46
  const candidates = [
47
47
  `${sourceRoot}/app/app.config.ts`,
48
48
  `${sourceRoot}/main.ts`,
@@ -53,29 +53,42 @@ function extractConfig(tree, sourceRoot, context) {
53
53
  const content = tree.readText(filePath);
54
54
  const provideMatch = PROVIDE_CALL_RE.exec(content);
55
55
  if (!provideMatch) {
56
- context.logger.warn(`⚠ provideThemeStack() not found in ${filePath}. Using library defaults.`);
56
+ context.logger.warn(`⚠ provideThemeStack() not found in ${filePath}. Using library defaults.\n` +
57
+ ` Tip: Add provideThemeStack({...}) to your providers for explicit control.`);
57
58
  break;
58
59
  }
59
60
  const opts = provideMatch[1]; // everything inside provideThemeStack(...)
60
61
  const mode = (_b = (_a = OPTION_MODE_RE.exec(opts)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : constants_1.DEFAULTS.mode;
61
62
  const storageKey = (_d = (_c = OPTION_KEY_RE.exec(opts)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : constants_1.DEFAULTS.storageKey;
62
63
  const defaultTheme = (_f = (_e = OPTION_DEFAULT_THEME_RE.exec(opts)) === null || _e === void 0 ? void 0 : _e[1]) !== null && _f !== void 0 ? _f : constants_1.DEFAULTS.defaultTheme;
64
+ const strategy = (_h = (_g = OPTION_STRATEGY_RE.exec(opts)) === null || _g === void 0 ? void 0 : _g[1]) !== null && _h !== void 0 ? _h : undefined;
65
+ // Extract themes array: ['light', 'dark', 'sunset'] → ['light', 'dark', 'sunset']
66
+ const themesRaw = (_k = (_j = OPTION_THEMES_RE.exec(opts)) === null || _j === void 0 ? void 0 : _j[1]) !== null && _k !== void 0 ? _k : '';
67
+ const themes = themesRaw
68
+ ? themesRaw
69
+ .split(',')
70
+ .map((t) => t.trim().replace(/^['"]|['"]$/g, ''))
71
+ .filter(Boolean)
72
+ : [...constants_1.DEFAULTS.themes];
63
73
  context.logger.info(` Detected mode : ${mode}`);
74
+ context.logger.info(` Detected strategy : ${strategy !== null && strategy !== void 0 ? strategy : '(not in code, using auto-detect)'}`);
64
75
  context.logger.info(` Detected storageKey : ${storageKey}`);
65
76
  context.logger.info(` Detected defaultTheme : ${defaultTheme}`);
66
- return { mode, storageKey, defaultTheme };
77
+ context.logger.info(` Detected themes : [${themes.join(', ')}]`);
78
+ return { mode, strategy, storageKey, defaultTheme, themes };
67
79
  }
68
80
  // Fallback to defaults if no config file found
69
81
  return {
70
82
  mode: constants_1.DEFAULTS.mode,
71
83
  storageKey: constants_1.DEFAULTS.storageKey,
72
84
  defaultTheme: constants_1.DEFAULTS.defaultTheme,
85
+ themes: [...constants_1.DEFAULTS.themes],
73
86
  };
74
87
  }
75
- // ── Script generation ─────────────────────────────────────────────────────────
88
+ // ── Anti-flash script generation ──────────────────────────────────────────────
76
89
  /**
77
90
  * Builds the minimal blocking inline script — identical logic to anti-flash.ts
78
- * but read-only (no themes list needed; generic regex validation).
91
+ * but kept here to avoid cross-directory dependencies in the schematic build.
79
92
  */
80
93
  function buildScript(config) {
81
94
  const { storageKey, defaultTheme, mode } = config;
@@ -92,40 +105,183 @@ function buildScript(config) {
92
105
  `if(t==='dark'||t==='light')e.style.setProperty('color-scheme',t);` +
93
106
  `}catch(x){}})();`);
94
107
  }
108
+ // ── Critters Trick div generation ─────────────────────────────────────────────
109
+ /**
110
+ * Generates the hidden divs for the Critters Trick based on the mode.
111
+ *
112
+ * When Angular builds for production, Critters inlines "critical" CSS.
113
+ * It determines "critical" by checking which selectors match elements in the HTML.
114
+ * By placing hidden divs with the theme class/attribute, we trick Critters into
115
+ * inlining ALL theme token blocks — achieving zero-network-request CSS for themes.
116
+ *
117
+ * @param themes - The list of themes to generate divs for (excludes 'system').
118
+ * @param mode - 'class' | 'attribute' | 'both'
119
+ */
120
+ function buildCrittersDivs(themes, mode) {
121
+ // 'system' is a meta-theme that resolves to 'light' or 'dark'; no CSS selector needed.
122
+ const renderableThemes = themes.filter((t) => t !== 'system');
123
+ const divs = renderableThemes
124
+ .map((theme) => {
125
+ if (mode === 'class') {
126
+ return ` <div class="${theme}"></div>`;
127
+ }
128
+ else if (mode === 'attribute') {
129
+ return ` <div data-theme="${theme}"></div>`;
130
+ }
131
+ else {
132
+ // 'both'
133
+ return ` <div class="${theme}" data-theme="${theme}"></div>`;
134
+ }
135
+ })
136
+ .join('\n');
137
+ return (`<!-- ngx-theme-stack critters-trick -->\n` +
138
+ ` <div id="ngx-theme-stack-critters-trick" hidden>\n${divs}\n </div>\n` +
139
+ ` <!-- /ngx-theme-stack critters-trick -->`);
140
+ }
95
141
  // ── index.html patching ───────────────────────────────────────────────────────
96
- function syncIndexHtml(tree, context, sourceRoot, config) {
142
+ function syncIndexHtml(tree, context, sourceRoot, config, strategy) {
97
143
  const candidates = [`${sourceRoot}/index.html`, 'public/index.html'].map((p) => p.startsWith('/') ? p.slice(1) : p);
98
144
  for (const path of candidates) {
99
145
  if (!tree.exists(path))
100
146
  continue;
101
- const content = tree.readText(path);
102
- if (!content.includes(SCRIPT_MARKER)) {
147
+ let content = tree.readText(path);
148
+ if (!content.includes('ngx-theme-stack anti-flash')) {
103
149
  context.logger.warn(`⚠ Anti-flash script marker not found in ${path}.\n` +
104
150
  ` Run 'ng add ngx-theme-stack' first, or add the script manually.`);
105
151
  return;
106
152
  }
153
+ // ── 1. Sync the anti-flash JS script ───────────────────────────────────
107
154
  const newScriptBlock = `<!-- ngx-theme-stack anti-flash -->\n <script>${buildScript(config)}</script>`;
108
- const updated = content.replace(SCRIPT_BLOCK_RE, newScriptBlock);
109
- if (updated === content) {
155
+ const updatedScript = content.replace(SCRIPT_BLOCK_RE, newScriptBlock);
156
+ if (updatedScript === content) {
110
157
  context.logger.warn(`⚠ Could not replace script block in ${path}. The format may have changed.`);
111
158
  return;
112
159
  }
113
- tree.overwrite(path, updated);
160
+ content = updatedScript;
114
161
  context.logger.info(`✔ Anti-flash script synced in ${path}`);
162
+ // ── 2. Sync the Critters Trick divs (only for 'critters' strategy) ─────
163
+ if (strategy === 'critters') {
164
+ const crittersBlock = buildCrittersDivs(config.themes, config.mode);
165
+ const CRITTERS_ID_RE = /<div id="ngx-theme-stack-critters-trick"[\s\S]*?<\/div>/;
166
+ if (CRITTERS_BLOCK_RE.test(content)) {
167
+ content = content.replace(CRITTERS_BLOCK_RE, crittersBlock);
168
+ context.logger.info(`✔ Critters Trick block updated in ${path}`);
169
+ }
170
+ else if (CRITTERS_ID_RE.test(content)) {
171
+ content = content.replace(CRITTERS_ID_RE, crittersBlock);
172
+ context.logger.info(`✔ Critters Trick div wrapped in ${path}`);
173
+ }
174
+ else if (content.includes(CRITTERS_MARKER)) {
175
+ content = content.replace(CRITTERS_MARKER, crittersBlock);
176
+ context.logger.info(`✔ Critters Trick divs injected in ${path}`);
177
+ }
178
+ else {
179
+ content = content.replace('</body>', ` ${crittersBlock}\n </body>`);
180
+ context.logger.info(`✔ Critters Trick block added before </body> in ${path}`);
181
+ }
182
+ }
183
+ else {
184
+ // blocking strategy: remove any existing Critters divs if present
185
+ if (CRITTERS_BLOCK_RE.test(content)) {
186
+ content = content.replace(CRITTERS_BLOCK_RE, '');
187
+ context.logger.info(`✔ Critters Trick divs removed (blocking strategy) in ${path}`);
188
+ }
189
+ }
190
+ tree.overwrite(path, content);
115
191
  return;
116
192
  }
117
193
  context.logger.warn(`⚠ Could not find index.html (tried: ${candidates.join(', ')}).`);
118
194
  }
195
+ // ── angular.json patching ───────────────────────────────────────────────────
196
+ function syncAngularJson(tree, context, projectName, strategy) {
197
+ var _a, _b;
198
+ const content = tree.read('/angular.json');
199
+ if (!content)
200
+ return;
201
+ const workspace = JSON.parse(content.toString());
202
+ const project = workspace.projects[projectName];
203
+ if (!project)
204
+ return;
205
+ const buildTarget = (_a = project.architect) === null || _a === void 0 ? void 0 : _a.build;
206
+ if (!buildTarget)
207
+ return;
208
+ const prodConfig = (_b = buildTarget.configurations) === null || _b === void 0 ? void 0 : _b.production;
209
+ if (!prodConfig)
210
+ return;
211
+ if (strategy === 'blocking') {
212
+ // Disable inlineCritical for blocking strategy
213
+ if (typeof prodConfig.optimization === 'object') {
214
+ prodConfig.optimization.styles = prodConfig.optimization.styles || {};
215
+ if (prodConfig.optimization.styles.inlineCritical !== false) {
216
+ prodConfig.optimization.styles.inlineCritical = false;
217
+ context.logger.info(`✔ Disabled inlineCritical in angular.json projects/${projectName} (production).`);
218
+ }
219
+ }
220
+ else {
221
+ // It's either boolean or undefined
222
+ prodConfig.optimization = {
223
+ styles: { inlineCritical: false },
224
+ };
225
+ context.logger.info(`✔ Disabled inlineCritical in angular.json projects/${projectName} (production).`);
226
+ }
227
+ }
228
+ else {
229
+ // Enable inlineCritical (or revert to default) for critters strategy
230
+ if (typeof prodConfig.optimization === 'object' && prodConfig.optimization.styles) {
231
+ if (prodConfig.optimization.styles.inlineCritical === false) {
232
+ prodConfig.optimization.styles.inlineCritical = true;
233
+ context.logger.info(`✔ Re-enabled inlineCritical in angular.json projects/${projectName} (production).`);
234
+ }
235
+ }
236
+ }
237
+ tree.overwrite('/angular.json', JSON.stringify(workspace, null, 2));
238
+ }
119
239
  // ── Schematic factory ─────────────────────────────────────────────────────────
120
240
  /**
121
241
  * `ng generate ngx-theme-stack:sync`
122
242
  *
123
243
  * Reads the current `provideThemeStack()` configuration from `app.config.ts`
124
- * and regenerates the anti-flash inline script in `index.html` to match.
244
+ * and regenerates:
125
245
  *
126
- * Run this whenever you change `mode`, `storageKey`, or `defaultTheme`
127
- * inside `provideThemeStack()` to keep the anti-flash script in sync.
246
+ * 1. **The anti-flash inline script** in `index.html` — keeping `storageKey`,
247
+ * `defaultTheme`, and `mode` in sync with the Angular provider.
248
+ *
249
+ * 2. **The Critters Trick divs** (if `strategy: 'critters'`) — hidden `<div>`
250
+ * elements that trick Angular's built-in CSS inliner (Critters) into treating
251
+ * all theme token blocks as "critical CSS", inlining them in the `<head>`
252
+ * at build time. This achieves zero-flash without any extra network requests.
253
+ *
254
+ * Run this whenever you change `mode`, `storageKey`, `defaultTheme`, or `themes`
255
+ * inside `provideThemeStack()`. Tip: add it as a `prebuild` script in package.json
256
+ * so it runs automatically before every build.
257
+ *
258
+ * @example
259
+ * // One-off sync
260
+ * ng generate ngx-theme-stack:sync
261
+ *
262
+ * @example
263
+ * // Automatic sync (recommended — add to package.json)
264
+ * "prebuild": "ng generate ngx-theme-stack:sync"
265
+ */
266
+ /**
267
+ * Auto-detects the strategy by checking if a Critters marker exists in index.html.
268
+ * This allows the prebuild command to run with zero extra flags.
128
269
  */
270
+ function detectStrategy(tree, sourceRoot, explicitStrategy) {
271
+ if (explicitStrategy)
272
+ return explicitStrategy;
273
+ const candidates = [`${sourceRoot}/index.html`, 'public/index.html'].map((p) => p.startsWith('/') ? p.slice(1) : p);
274
+ for (const path of candidates) {
275
+ if (!tree.exists(path))
276
+ continue;
277
+ const content = tree.readText(path);
278
+ // If either the full block OR the bare marker comment exist → critters
279
+ if (content.includes('ngx-theme-stack critters-trick'))
280
+ return 'critters';
281
+ return 'blocking';
282
+ }
283
+ return 'critters'; // safe default
284
+ }
129
285
  function sync(options) {
130
286
  return (tree, context) => {
131
287
  var _a, _b;
@@ -140,13 +296,18 @@ function sync(options) {
140
296
  throw new Error(`Project "${projectName}" not found in angular.json.`);
141
297
  }
142
298
  const sourceRoot = project.sourceRoot || `${(_b = project.root) !== null && _b !== void 0 ? _b : ''}/src`;
299
+ const config = extractConfig(tree, sourceRoot, context);
300
+ const strategy = (options.strategy || config.strategy || detectStrategy(tree, sourceRoot));
143
301
  context.logger.info('');
144
- context.logger.info(`🔄 ngx-theme-stack sync [project: ${projectName}]`);
302
+ context.logger.info(`🔄 ngx-theme-stack sync [project: ${projectName}, strategy: ${strategy}]`);
145
303
  context.logger.info('');
146
- const config = extractConfig(tree, sourceRoot, context);
147
- syncIndexHtml(tree, context, sourceRoot, config);
304
+ syncIndexHtml(tree, context, sourceRoot, config, strategy);
305
+ syncAngularJson(tree, context, projectName, strategy);
148
306
  context.logger.info('');
149
- context.logger.info('✅ Done! The anti-flash script is now in sync with your provider config.');
307
+ context.logger.info('✅ Done! The anti-flash setup is now in sync with your provider config.');
308
+ if (strategy === 'critters') {
309
+ context.logger.info(' Critters Trick: theme CSS will be automatically inlined at build time.');
310
+ }
150
311
  context.logger.info('');
151
312
  };
152
313
  }