@pre.style/core 3.1.0-alpha.5 → 3.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -759,7 +759,7 @@ Namespace imports still let bundlers drop the classes you don't use. **Remove th
759
759
 
760
760
  ## Sharing values with prepended files
761
761
 
762
- Use `prependFiles`, in the `pre.style` field of your `package.json`, to add files to the start of every block and stylesheet file, for custom properties, SCSS variables, mixins, and so on. Each file is read once per build.
762
+ Use `prependFiles`, in the `pre.style` field of your `package.json`, to add files to the start of every block and stylesheet file, for custom properties, SCSS variables, mixins, and so on. Each file is read once per build. In dev, saving one updates every block and stylesheet file it's prepended to. Changes to the `pre.style` field itself need a restart of the dev server, which the plugin warns about.
763
763
 
764
764
  ```json
765
765
  {
@@ -83,6 +83,8 @@ export declare class Compiler {
83
83
  classNames(sheet: Stylesheet): string[];
84
84
  usages(classNames: Iterable<string>): ClassUsage[];
85
85
  forgetSources(file: string): void;
86
+ prependFiles(): string[];
87
+ fileChanged(file: string): boolean;
86
88
  stylesheet(sheet: Stylesheet): CompiledStylesheet;
87
89
  css(): Printed;
88
90
  cnTable(): CnTable;
@@ -171,9 +171,10 @@ export class Compiler {
171
171
  };
172
172
  });
173
173
  }
174
- // The files a block's class string depends on, through `composes … from`.
174
+ // The files a block's class string depends on: its prepended files, and
175
+ // files it composes from.
175
176
  dependencies(block) {
176
- const dependencies = [];
177
+ const dependencies = [...this.#library(block.file).prependFiles[block.lang]];
177
178
  this.#resolveBlock(block, this.#parse(block), dependencies);
178
179
  return [...new Set(dependencies)];
179
180
  }
@@ -218,10 +219,45 @@ export class Compiler {
218
219
  if (source.file === file)
219
220
  record.sources.delete(key);
220
221
  }
222
+ // Every prepended file, the app's and each library's, as absolute paths.
223
+ prependFiles() {
224
+ return [...new Set(this.#prepends().flatMap(({ files: { css, scss } }) => [...css, ...scss]))];
225
+ }
226
+ // Call when a file changes. Drops the places it uses atoms (see
227
+ // `forgetSources`) and, if it's a prepended file, reads it again, so blocks
228
+ // and stylesheet files compiled after this use its new code. Returns whether
229
+ // it was a prepended file.
230
+ fileChanged(file) {
231
+ this.forgetSources(file);
232
+ let changed = false;
233
+ for (const prepended of this.#prepends()) {
234
+ if (![...prepended.files.css, ...prepended.files.scss].includes(file))
235
+ continue;
236
+ try {
237
+ prepended.code = readPrependCode(prepended.files);
238
+ }
239
+ catch (error) {
240
+ // Kept as it was, until the file can be read again.
241
+ this.#parseOptions.warn(`Can't read ${file}, a prepended file, so blocks use its last contents: ${error.message}`);
242
+ continue;
243
+ }
244
+ changed = true;
245
+ }
246
+ if (changed) {
247
+ // Parsed with the old code. Cache keys hold the code they parsed, but
248
+ // not the code prepended to it.
249
+ this.#blocks.clear();
250
+ this.#stylesheets.clear();
251
+ }
252
+ return changed;
253
+ }
221
254
  // Each class of a stylesheet file, with its class string.
222
255
  stylesheet(sheet) {
223
256
  const parsed = this.#parseSheet(sheet);
224
- const dependencies = [...parsed.dependencies];
257
+ const dependencies = [
258
+ ...this.#library(sheet.file).prependFiles[sheet.lang],
259
+ ...parsed.dependencies,
260
+ ];
225
261
  const exports = [];
226
262
  for (const name of parsed.classes.keys())
227
263
  exports.push([
@@ -287,6 +323,10 @@ export class Compiler {
287
323
  variables[this.#names.variables.set(key)] = property ? this.#propertyName(property) : '';
288
324
  return variables;
289
325
  }
326
+ // The app's prepended files, then each library's.
327
+ #prepends() {
328
+ return [this.#prepend, ...this.#libraries.map((library) => library.prepend)];
329
+ }
290
330
  // A library's blocks and files get its prepended files and keyframes
291
331
  // setting instead of the app's.
292
332
  #library(file) {
@@ -300,9 +340,10 @@ export class Compiler {
300
340
  : this.#libraries[library];
301
341
  return {
302
342
  library,
343
+ prependFiles: prepend.files,
303
344
  options: {
304
345
  ...this.#parseOptions,
305
- prepend,
346
+ prepend: prepend.code,
306
347
  keyframeLocalization: keyframeLocalization ?? false,
307
348
  customPropertyLocalization: customPropertyLocalization ?? false,
308
349
  },
@@ -661,13 +702,19 @@ function layerRule(layer) {
661
702
  }
662
703
  // A stylesheet file, for positions and source maps.
663
704
  const fileBlock = (file) => ({ code: '', lang: 'css', file, line: 1, column: 1 });
664
- // Reads prepended files, relative to `dir`, once.
705
+ // Reads prepended files, relative to `dir`.
665
706
  function readPrepend(dir, files) {
666
- const read = (list = []) => list.map((file) => readFileSync(resolvePath(dir, file), 'utf8')).join('\n');
667
- return Array.isArray(files)
668
- ? { css: read(files), scss: read(files) }
669
- : { css: read(files?.css), scss: read(files?.scss) };
707
+ const resolve = (list = []) => list.map((file) => resolvePath(dir, file));
708
+ const paths = Array.isArray(files)
709
+ ? { css: resolve(files), scss: resolve(files) }
710
+ : { css: resolve(files?.css), scss: resolve(files?.scss) };
711
+ return { files: paths, code: readPrependCode(paths) };
670
712
  }
713
+ const readFiles = (files) => files.map((file) => readFileSync(file, 'utf8')).join('\n');
714
+ const readPrependCode = (files) => ({
715
+ css: readFiles(files.css),
716
+ scss: readFiles(files.scss),
717
+ });
671
718
  const count = (map, key) => map.set(key, (map.get(key) ?? 0) + 1);
672
719
  // The at-rules and selector variant an atom is under. Empty for atoms on the
673
720
  // element.
@@ -6,6 +6,6 @@ export type { Slot } from './interpolate.ts';
6
6
  export type { Printed } from './print.ts';
7
7
  export { checkField, readConfig } from './config.ts';
8
8
  export type { PackageConfig } from './config.ts';
9
- export { findLibraries } from './libraries.ts';
9
+ export { configFiles, findLibraries } from './libraries.ts';
10
10
  export type { LibraryConfig, ResolvedLibrary } from './libraries.ts';
11
11
  export { isIdentifier } from './stylesheet.ts';
@@ -1,5 +1,5 @@
1
1
  export { Compiler, createCompiler, interpolatedBlock } from './compiler.js';
2
2
  export { PreStyleError } from './block.js';
3
3
  export { checkField, readConfig } from './config.js';
4
- export { findLibraries } from './libraries.js';
4
+ export { configFiles, findLibraries } from './libraries.js';
5
5
  export { isIdentifier } from './stylesheet.js';
@@ -16,3 +16,6 @@ export interface ResolvedLibrary {
16
16
  customPropertyLocalization?: boolean;
17
17
  }
18
18
  export declare function findLibraries(root: string, overrides?: Record<string, LibraryConfig | boolean>, warn?: (message: string) => void): ResolvedLibrary[];
19
+ export declare const configFiles: (appDir: string, libraries: {
20
+ dir: string;
21
+ }[]) => string[];
@@ -81,3 +81,9 @@ export function findLibraries(root, overrides = {}, warn = () => { }) {
81
81
  visit(name, root);
82
82
  return libraries;
83
83
  }
84
+ // The package.json files whose `pre.style` fields configure the compiler: the
85
+ // app's and each library's.
86
+ export const configFiles = (appDir, libraries) => [
87
+ join(appDir, 'package.json'),
88
+ ...libraries.map(({ dir }) => join(dir, 'package.json')),
89
+ ];
@@ -10,17 +10,16 @@
10
10
  // "contentMappers": [{ "package": "@pre.style/core", "extensions": [".css", ".scss"] }]
11
11
  import { createHash } from 'node:crypto';
12
12
  import { existsSync, readFileSync } from 'node:fs';
13
- import { dirname, join, resolve } from 'node:path';
13
+ import { dirname } from 'node:path';
14
14
  import { PreStyleError } from './block.js';
15
15
  import { createCompiler } from './compiler.js';
16
16
  import { readConfig } from './config.js';
17
- import { findLibraries } from './libraries.js';
17
+ import { configFiles, findLibraries } from './libraries.js';
18
18
  import { isIdentifier } from './stylesheet.js';
19
19
  // Each open project's compiler, by its handle.
20
20
  const projects = new Map();
21
21
  // Diagnostic codes, for errors in a stylesheet file.
22
22
  const INVALID_STYLESHEET = 9001;
23
- const prependPaths = (dir, files) => (Array.isArray(files) ? files : [...(files?.css ?? []), ...(files?.scss ?? [])]).map((file) => resolve(dir, file));
24
23
  async function openProject(params) {
25
24
  // The project's package.json, nearest its tsconfig.json.
26
25
  const root = params.configFileName ? dirname(params.configFileName) : process.cwd();
@@ -41,15 +40,10 @@ async function openProject(params) {
41
40
  warn: () => { },
42
41
  });
43
42
  projects.set(params.projectHandle, compiler);
44
- await compiler;
45
43
  // Changes to these files change which classes files export.
46
44
  const watchedFiles = [
47
- join(app.dir, 'package.json'),
48
- ...prependPaths(app.dir, app.config.prependFiles),
49
- ...libraries.flatMap(({ dir, prependFiles }) => [
50
- join(dir, 'package.json'),
51
- ...prependPaths(dir, prependFiles),
52
- ]),
45
+ ...configFiles(app.dir, libraries),
46
+ ...(await compiler).prependFiles(),
53
47
  ].filter((file) => existsSync(file));
54
48
  const hash = createHash('sha256');
55
49
  for (const file of watchedFiles)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pre.style/core",
3
- "version": "3.1.0-alpha.5",
3
+ "version": "3.1.0-alpha.6",
4
4
  "description": "Zero-runtime CSS-in-JS that compiles css and scss blocks into atomic CSS classes.",
5
5
  "keywords": [
6
6
  "atomic-css",