@rokkit/themes 1.1.14 → 1.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build.mjs CHANGED
@@ -41,7 +41,9 @@ const uno = await createGenerator({
41
41
  })
42
42
  ],
43
43
  shortcuts: [
44
- ['skin-default', { ...theme.getPalette(), ...theme.getNamedTokens() }],
44
+ // NOTE: the former `skin-default` shortcut was removed — palette.css no
45
+ // longer `@apply`s it. The default skin's named-token vars now come from
46
+ // the preset's `:root` preflight (see @rokkit/unocss buildPreflights).
45
47
  ...theme.getShortcuts('surface'),
46
48
  ...theme.getShortcuts('primary'),
47
49
  ...theme.getShortcuts('secondary'),
@@ -176,16 +178,53 @@ function splitTopLevelSelectors(text) {
176
178
  return parts
177
179
  }
178
180
 
181
+ // ─── Regression guard: no @apply may survive into dist ────────────────────────
182
+
183
+ /**
184
+ * Strip /* … *​/ block comments so the scan matches real CSS, not prose
185
+ * (some files document the @apply pitfall in comments). Mirrors the
186
+ * `stripComments` helper in spec/coverage.spec.js.
187
+ */
188
+ const stripComments = (s) => s.replace(/\/\*[\s\S]*?\*\//g, '')
189
+
190
+ /**
191
+ * Fail the build if any real `@apply` directive survived into a dist file.
192
+ *
193
+ * A leftover `@apply` means a utility didn't resolve (e.g. a named-token
194
+ * `/opacity` shortcut, which UnoCSS can't expand) and would ship raw to
195
+ * consumers — triggering `[lightningcss minify] Unknown at rule: @apply`
196
+ * and rendering nothing. This is the recurrence guard for issue #135.
197
+ */
198
+ function assertNoApply(outputName) {
199
+ const content = readFileSync(join(distDir, outputName), 'utf-8') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — outputName is derived from a hardcoded string array, not user input
200
+ const lines = stripComments(content).split('\n')
201
+ const offending = []
202
+ lines.forEach((line, i) => {
203
+ if (/@apply\b/.test(line)) offending.push(` dist/${outputName}:${i + 1} ${line.trim()}`)
204
+ })
205
+ if (offending.length > 0) {
206
+ throw new Error(
207
+ `Unresolved @apply leaked into dist/${outputName} (issue #135 recurrence).\n` +
208
+ `These utilities did not resolve during build — rewrite them to raw CSS ` +
209
+ `(e.g. color-mix for named-token /opacity):\n${offending.join('\n')}`
210
+ )
211
+ }
212
+ }
213
+
179
214
  // ─── Build ────────────────────────────────────────────────────────────────────
180
215
 
181
216
  const srcDir = join(__dirname, 'src')
182
217
  const distDir = join(__dirname, 'dist')
183
218
 
219
+ /** Names of every dist file emitted by this build, scanned by the guard. */
220
+ const emitted = []
221
+
184
222
  async function buildFile(inputPath, outputName, label) {
185
223
  const fullCSS = resolveImports(inputPath)
186
224
  const compiled = await processCSS(fullCSS, outputName)
187
225
  const fixed = fixModeSelectors(compiled)
188
226
  writeFileSync(join(distDir, outputName), fixed, 'utf-8') // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — outputName is derived from a hardcoded string array, not user input
227
+ emitted.push(outputName)
189
228
  console.log(`✓ dist/${outputName} (${label})`)
190
229
  }
191
230
 
@@ -200,6 +239,7 @@ async function build() {
200
239
  const compiledBase = await processCSS(baseCSS, 'base.css')
201
240
  const baseFull = fixModeSelectors(compiledPalette + '\n' + zScaleCSS + '\n' + compiledBase)
202
241
  writeFileSync(join(distDir, 'base.css'), baseFull, 'utf-8')
242
+ emitted.push('base.css')
203
243
  console.log('✓ dist/base.css (structural styles + palette defaults)')
204
244
 
205
245
  // Per-theme files
@@ -225,8 +265,12 @@ async function build() {
225
265
  // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal — name is from a hardcoded string array, not user input
226
266
  const bundleParts = allThemes.map((name) => readFileSync(join(distDir, `${name}.css`), 'utf-8'))
227
267
  writeFileSync(join(distDir, 'index.css'), bundleParts.join('\n'), 'utf-8')
268
+ emitted.push('index.css')
228
269
  console.log('✓ dist/index.css (full bundle)')
229
270
 
271
+ // Regression guard (#135): no unresolved @apply may ship in dist.
272
+ for (const outputName of emitted) assertNoApply(outputName)
273
+
230
274
  console.log('\n@rokkit/themes build complete.')
231
275
  }
232
276