@knighted/css 1.0.0-rc.6 → 1.0.0-rc.8

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.
@@ -1,4 +1,5 @@
1
1
  export const COMBINED_QUERY_FLAG = 'combined';
2
+ export const TYPES_QUERY_FLAG = 'types';
2
3
  export const NAMED_ONLY_QUERY_FLAGS = ['named-only', 'no-default'];
3
4
  export function splitQuery(query) {
4
5
  const trimmed = query.startsWith('?') ? query.slice(1) : query;
@@ -25,6 +26,9 @@ export function buildSanitizedQuery(query) {
25
26
  if (isQueryFlag(part, 'knighted-css')) {
26
27
  return false;
27
28
  }
29
+ if (isQueryFlag(part, TYPES_QUERY_FLAG)) {
30
+ return false;
31
+ }
28
32
  if (NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag))) {
29
33
  return false;
30
34
  }
@@ -32,6 +36,22 @@ export function buildSanitizedQuery(query) {
32
36
  });
33
37
  return entries.length > 0 ? `?${entries.join('&')}` : '';
34
38
  }
39
+ export function hasQueryFlag(query, flag) {
40
+ if (!query)
41
+ return false;
42
+ const entries = splitQuery(query);
43
+ if (entries.length === 0)
44
+ return false;
45
+ return entries.some(part => isQueryFlag(part, flag));
46
+ }
47
+ function safeDecode(value) {
48
+ try {
49
+ return decodeURIComponent(value);
50
+ }
51
+ catch {
52
+ return value;
53
+ }
54
+ }
35
55
  export function shouldForwardDefaultExport(request) {
36
56
  const [pathPart] = request.split('?');
37
57
  if (!pathPart)
@@ -42,6 +62,18 @@ export function shouldForwardDefaultExport(request) {
42
62
  }
43
63
  return true;
44
64
  }
65
+ export function hasCombinedQuery(query) {
66
+ return hasQueryFlag(query, COMBINED_QUERY_FLAG);
67
+ }
68
+ export function hasNamedOnlyQueryFlag(query) {
69
+ return NAMED_ONLY_QUERY_FLAGS.some(flag => hasQueryFlag(query, flag));
70
+ }
71
+ export function determineSelectorVariant(query) {
72
+ if (hasCombinedQuery(query)) {
73
+ return hasNamedOnlyQueryFlag(query) ? 'combinedWithoutDefault' : 'combined';
74
+ }
75
+ return 'types';
76
+ }
45
77
  export function shouldEmitCombinedDefault(options) {
46
78
  if (options.skipSyntheticDefault) {
47
79
  return false;
@@ -60,4 +92,5 @@ export function shouldEmitCombinedDefault(options) {
60
92
  export const __loaderInternals = {
61
93
  buildSanitizedQuery,
62
94
  shouldEmitCombinedDefault,
95
+ determineSelectorVariant,
63
96
  };
@@ -0,0 +1,3 @@
1
+ declare const DEFAULT_STABLE_NAMESPACE = "knighted";
2
+ export declare function resolveStableNamespace(optionNamespace?: string): string;
3
+ export { DEFAULT_STABLE_NAMESPACE };
@@ -0,0 +1,8 @@
1
+ const DEFAULT_STABLE_NAMESPACE = 'knighted';
2
+ export function resolveStableNamespace(optionNamespace) {
3
+ if (typeof optionNamespace === 'string') {
4
+ return optionNamespace;
5
+ }
6
+ return DEFAULT_STABLE_NAMESPACE;
7
+ }
8
+ export { DEFAULT_STABLE_NAMESPACE };
@@ -0,0 +1,19 @@
1
+ export interface StableSelectorsLiteralResult {
2
+ literal: string;
3
+ selectorMap: Map<string, string>;
4
+ }
5
+ export declare function buildStableSelectorsLiteral(options: {
6
+ css: string;
7
+ namespace: string;
8
+ resourcePath: string;
9
+ emitWarning: (message: string) => void;
10
+ }): StableSelectorsLiteralResult;
11
+ export declare function collectStableSelectors(css: string, namespace: string, filename?: string): Map<string, string>;
12
+ declare function collectStableSelectorsByRegex(css: string, namespace: string): Map<string, string>;
13
+ export declare function formatStableSelectorMap(map: Map<string, string>): string;
14
+ export declare const __stableSelectorsLiteralInternals: {
15
+ collectStableSelectors: typeof collectStableSelectors;
16
+ collectStableSelectorsByRegex: typeof collectStableSelectorsByRegex;
17
+ formatStableSelectorMap: typeof formatStableSelectorMap;
18
+ };
19
+ export {};
@@ -0,0 +1,98 @@
1
+ import { transform as lightningTransform } from 'lightningcss';
2
+ import { escapeRegex, serializeSelector } from './helpers.js';
3
+ export function buildStableSelectorsLiteral(options) {
4
+ const trimmedNamespace = options.namespace.trim();
5
+ if (!trimmedNamespace) {
6
+ options.emitWarning(`stableSelectors requested for ${options.resourcePath} but "stableNamespace" resolved to an empty value.`);
7
+ return {
8
+ literal: 'export const stableSelectors = {} as const;\n',
9
+ selectorMap: new Map(),
10
+ };
11
+ }
12
+ const selectorMap = collectStableSelectors(options.css, trimmedNamespace, options.resourcePath);
13
+ if (selectorMap.size === 0) {
14
+ options.emitWarning(`stableSelectors requested for ${options.resourcePath} but no selectors matched namespace "${trimmedNamespace}".`);
15
+ }
16
+ return {
17
+ literal: `export const stableSelectors = ${formatStableSelectorMap(selectorMap)} as const;\n`,
18
+ selectorMap,
19
+ };
20
+ }
21
+ export function collectStableSelectors(css, namespace, filename) {
22
+ if (!namespace)
23
+ return new Map();
24
+ const astResult = collectStableSelectorsFromAst(css, namespace, filename);
25
+ if (astResult) {
26
+ return astResult;
27
+ }
28
+ return collectStableSelectorsByRegex(css, namespace);
29
+ }
30
+ function collectStableSelectorsFromAst(css, namespace, filename) {
31
+ try {
32
+ const tokens = new Map();
33
+ const escaped = escapeRegex(namespace);
34
+ const pattern = new RegExp(`\\.${escaped}-([A-Za-z0-9_-]+)`, 'g');
35
+ lightningTransform({
36
+ filename: filename ?? 'knighted-types-probe.css',
37
+ code: Buffer.from(css),
38
+ minify: false,
39
+ visitor: {
40
+ Rule: {
41
+ style(rule) {
42
+ const target = Array.isArray(rule?.selectors)
43
+ ? rule
44
+ : rule?.value && Array.isArray(rule.value.selectors)
45
+ ? rule.value
46
+ : undefined;
47
+ if (!target)
48
+ return rule;
49
+ for (const selector of target.selectors) {
50
+ const selectorStr = serializeSelector(selector);
51
+ pattern.lastIndex = 0;
52
+ let match;
53
+ while ((match = pattern.exec(selectorStr)) !== null) {
54
+ const token = match[1];
55
+ if (!token)
56
+ continue;
57
+ tokens.set(token, `${namespace}-${token}`);
58
+ }
59
+ }
60
+ return rule;
61
+ },
62
+ },
63
+ },
64
+ });
65
+ return tokens;
66
+ }
67
+ catch {
68
+ return undefined;
69
+ }
70
+ }
71
+ function collectStableSelectorsByRegex(css, namespace) {
72
+ const escaped = escapeRegex(namespace);
73
+ const pattern = new RegExp(`\\.${escaped}-([A-Za-z0-9_-]+)`, 'g');
74
+ const tokens = new Map();
75
+ let match;
76
+ while ((match = pattern.exec(css)) !== null) {
77
+ const token = match[1];
78
+ if (!token)
79
+ continue;
80
+ tokens.set(token, `${namespace}-${token}`);
81
+ }
82
+ return tokens;
83
+ }
84
+ export function formatStableSelectorMap(map) {
85
+ if (map.size === 0) {
86
+ return 'Object.freeze({})';
87
+ }
88
+ const entries = Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b));
89
+ const lines = entries.map(([token, selector]) => {
90
+ return ` ${JSON.stringify(token)}: ${JSON.stringify(selector)}`;
91
+ });
92
+ return `Object.freeze({\n${lines.join(',\n')}\n})`;
93
+ }
94
+ export const __stableSelectorsLiteralInternals = {
95
+ collectStableSelectors,
96
+ collectStableSelectorsByRegex,
97
+ formatStableSelectorMap,
98
+ };
@@ -6,6 +6,13 @@ declare module '*?knighted-css' {
6
6
  export const knightedCss: string
7
7
  }
8
8
 
9
+ type KnightedCssStableSelectorMap = Readonly<Record<string, string>>
10
+
11
+ declare module '*?knighted-css&types' {
12
+ export const knightedCss: string
13
+ export const stableSelectors: KnightedCssStableSelectorMap
14
+ }
15
+
9
16
  /**
10
17
  * Ambient declaration for combined loader imports (e.g. "./file.tsx?knighted-css&combined").
11
18
  * These modules behave like the original module with an additional `knightedCss` export.
@@ -20,10 +27,35 @@ declare module '*?knighted-css&combined' {
20
27
  export const knightedCss: string
21
28
  }
22
29
 
23
- declare module '*?knighted-css&combined&named-only*' {
30
+ declare module '*?knighted-css&combined&named-only' {
31
+ const combined: KnightedCssCombinedModule<Record<string, unknown>>
32
+ export default combined
33
+ export const knightedCss: string
34
+ }
35
+
36
+ declare module '*?knighted-css&combined&no-default' {
37
+ const combined: KnightedCssCombinedModule<Record<string, unknown>>
38
+ export default combined
24
39
  export const knightedCss: string
25
40
  }
26
41
 
27
- declare module '*?knighted-css&combined&no-default*' {
42
+ declare module '*?knighted-css&combined&types' {
43
+ const combined: KnightedCssCombinedModule<Record<string, unknown>>
44
+ export default combined
45
+ export const knightedCss: string
46
+ export const stableSelectors: KnightedCssStableSelectorMap
47
+ }
48
+
49
+ declare module '*?knighted-css&combined&named-only&types' {
50
+ const combined: KnightedCssCombinedModule<Record<string, unknown>>
51
+ export default combined
52
+ export const knightedCss: string
53
+ export const stableSelectors: KnightedCssStableSelectorMap
54
+ }
55
+
56
+ declare module '*?knighted-css&combined&no-default&types' {
57
+ const combined: KnightedCssCombinedModule<Record<string, unknown>>
58
+ export default combined
28
59
  export const knightedCss: string
60
+ export const stableSelectors: KnightedCssStableSelectorMap
29
61
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/css",
3
- "version": "1.0.0-rc.6",
3
+ "version": "1.0.0-rc.8",
4
4
  "description": "A build-time utility that traverses JavaScript/TypeScript module dependency graphs to extract, compile, and optimize all imported CSS into a single, in-memory string.",
5
5
  "type": "module",
6
6
  "main": "./dist/css.js",
@@ -13,6 +13,9 @@
13
13
  "loader-queries": [
14
14
  "./loader-queries.d.ts"
15
15
  ],
16
+ "generate-types": [
17
+ "./dist/generateTypes.d.ts"
18
+ ],
16
19
  "*": [
17
20
  "./types.d.ts"
18
21
  ]
@@ -33,6 +36,11 @@
33
36
  "types": "./loader-queries.d.ts",
34
37
  "default": "./loader-queries.d.ts"
35
38
  },
39
+ "./generate-types": {
40
+ "types": "./dist/generateTypes.d.ts",
41
+ "import": "./dist/generateTypes.js",
42
+ "default": "./dist/generateTypes.js"
43
+ },
36
44
  "./stableSelectors": {
37
45
  "types": "./dist/stableSelectors.d.ts",
38
46
  "import": "./dist/stableSelectors.js",
@@ -58,16 +66,20 @@
58
66
  "vanilla-extract",
59
67
  "lightningcss"
60
68
  ],
69
+ "bin": {
70
+ "knighted-css-generate-types": "./bin/generate-types.js"
71
+ },
61
72
  "scripts": {
62
- "build": "duel",
63
- "check-types": "tsc --noEmit",
73
+ "build": "duel && node ./scripts/copy-types-stub.js",
74
+ "check-types": "tsc --noEmit --project tsconfig.json && tsc --noEmit --project tsconfig.tests.json",
64
75
  "test": "c8 --reporter=text --reporter=text-summary --reporter=lcov --include \"src/**/*.ts\" tsx --test test/**/*.test.ts",
65
76
  "prepack": "npm run build"
66
77
  },
67
78
  "dependencies": {
68
79
  "dependency-tree": "^11.2.0",
69
80
  "es-module-lexer": "^2.0.0",
70
- "lightningcss": "^1.30.2"
81
+ "lightningcss": "^1.30.2",
82
+ "node-module-type": "^1.0.1"
71
83
  },
72
84
  "overrides": {
73
85
  "module-lookup-amd": {
@@ -94,7 +106,9 @@
94
106
  "dist",
95
107
  "loader-queries.d.ts",
96
108
  "types.d.ts",
97
- "stable"
109
+ "stable",
110
+ "bin",
111
+ "types-stub"
98
112
  ],
99
113
  "author": "KCM <knightedcodemonkey@gmail.com>",
100
114
  "license": "MIT",
@@ -0,0 +1,5 @@
1
+ // Placeholder stub for @knighted/css stable selector declarations.
2
+ // Running `knighted-css-generate-types` replaces this file with project-specific
3
+ // references into the generated `.knighted-css` manifest.
4
+
5
+ export {}
package/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  /// <reference path="./loader-queries.d.ts" />
2
+ /// <reference path="./types-stub/index.d.ts" />
2
3
 
3
4
  export * from './dist/css.js'