@knighted/css 1.0.0-rc.1 → 1.0.0-rc.10

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 (51) hide show
  1. package/bin/generate-types.js +31 -0
  2. package/dist/cjs/css.cjs +100 -22
  3. package/dist/cjs/css.d.cts +5 -6
  4. package/dist/cjs/generateTypes.cjs +636 -0
  5. package/dist/cjs/generateTypes.d.cts +104 -0
  6. package/dist/cjs/loader.cjs +65 -55
  7. package/dist/cjs/loader.d.cts +1 -0
  8. package/dist/cjs/loaderInternals.cjs +108 -0
  9. package/dist/cjs/loaderInternals.d.cts +23 -0
  10. package/dist/cjs/moduleGraph.cjs +431 -0
  11. package/dist/cjs/moduleGraph.d.cts +15 -0
  12. package/dist/cjs/moduleInfo.cjs +62 -0
  13. package/dist/cjs/moduleInfo.d.cts +10 -0
  14. package/dist/cjs/sassInternals.cjs +135 -0
  15. package/dist/cjs/sassInternals.d.cts +25 -0
  16. package/dist/cjs/stableNamespace.cjs +12 -0
  17. package/dist/cjs/stableNamespace.d.cts +3 -0
  18. package/dist/cjs/stableSelectors.cjs +44 -0
  19. package/dist/cjs/stableSelectors.d.cts +13 -0
  20. package/dist/cjs/stableSelectorsLiteral.cjs +104 -0
  21. package/dist/cjs/stableSelectorsLiteral.d.cts +19 -0
  22. package/dist/cjs/types.cjs +2 -0
  23. package/dist/cjs/types.d.cts +4 -0
  24. package/dist/css.d.ts +5 -6
  25. package/dist/css.js +101 -23
  26. package/dist/generateTypes.d.ts +104 -0
  27. package/dist/generateTypes.js +628 -0
  28. package/dist/loader.d.ts +1 -0
  29. package/dist/loader.js +63 -53
  30. package/dist/loaderInternals.d.ts +23 -0
  31. package/dist/loaderInternals.js +96 -0
  32. package/dist/moduleGraph.d.ts +15 -0
  33. package/dist/moduleGraph.js +425 -0
  34. package/dist/moduleInfo.d.ts +10 -0
  35. package/dist/moduleInfo.js +55 -0
  36. package/dist/sassInternals.d.ts +25 -0
  37. package/dist/sassInternals.js +124 -0
  38. package/dist/stableNamespace.d.ts +3 -0
  39. package/dist/stableNamespace.js +8 -0
  40. package/dist/stableSelectors.d.ts +13 -0
  41. package/dist/stableSelectors.js +36 -0
  42. package/dist/stableSelectorsLiteral.d.ts +19 -0
  43. package/dist/stableSelectorsLiteral.js +98 -0
  44. package/dist/types.d.ts +4 -0
  45. package/dist/types.js +1 -0
  46. package/loader-queries.d.ts +61 -0
  47. package/package.json +58 -8
  48. package/stable/_index.scss +57 -0
  49. package/stable/stable.css +15 -0
  50. package/types-stub/index.d.ts +5 -0
  51. package/types.d.ts +4 -0
package/dist/loader.js CHANGED
@@ -1,10 +1,25 @@
1
1
  import { cssWithMeta, compileVanillaModule } from './css.js';
2
+ import { detectModuleDefaultExport } from './moduleInfo.js';
3
+ import { buildSanitizedQuery, hasCombinedQuery, hasNamedOnlyQueryFlag, hasQueryFlag, shouldEmitCombinedDefault, shouldForwardDefaultExport, TYPES_QUERY_FLAG, } from './loaderInternals.js';
4
+ import { buildStableSelectorsLiteral } from './stableSelectorsLiteral.js';
5
+ import { resolveStableNamespace } from './stableNamespace.js';
2
6
  const DEFAULT_EXPORT_NAME = 'knightedCss';
3
- const COMBINED_QUERY_FLAG = 'combined';
4
7
  const loader = async function loader(source) {
5
- const { cssOptions, vanillaOptions } = resolveLoaderOptions(this);
8
+ const { cssOptions, vanillaOptions, stableNamespace: optionNamespace, } = resolveLoaderOptions(this);
9
+ const resolvedNamespace = resolveStableNamespace(optionNamespace);
10
+ const typesRequested = hasQueryFlag(this.resourceQuery, TYPES_QUERY_FLAG);
6
11
  const css = await extractCss(this, cssOptions);
7
- const injection = buildInjection(css);
12
+ const stableSelectorsLiteral = typesRequested
13
+ ? buildStableSelectorsLiteral({
14
+ css,
15
+ namespace: resolvedNamespace,
16
+ resourcePath: this.resourcePath,
17
+ emitWarning: message => emitKnightedWarning(this, message),
18
+ })
19
+ : undefined;
20
+ const injection = buildInjection(css, {
21
+ stableSelectorsLiteral: stableSelectorsLiteral?.literal,
22
+ });
8
23
  const isStyleModule = this.resourcePath.endsWith('.css.ts');
9
24
  if (isStyleModule) {
10
25
  const { source: compiledSource } = await compileVanillaModule(this.resourcePath, cssOptions.cwd ?? this.rootContext ?? process.cwd(), cssOptions.peerResolver);
@@ -43,14 +58,38 @@ export const pitch = function pitch() {
43
58
  return;
44
59
  }
45
60
  const request = buildProxyRequest(this);
46
- const { cssOptions } = resolveLoaderOptions(this);
47
- return extractCss(this, cssOptions).then(css => createCombinedModule(request, css));
61
+ const { cssOptions, stableNamespace: optionNamespace } = resolveLoaderOptions(this);
62
+ const typesRequested = hasQueryFlag(this.resourceQuery, TYPES_QUERY_FLAG);
63
+ const resolvedNamespace = resolveStableNamespace(optionNamespace);
64
+ const skipSyntheticDefault = hasNamedOnlyQueryFlag(this.resourceQuery);
65
+ const defaultSignalPromise = skipSyntheticDefault
66
+ ? Promise.resolve('unknown')
67
+ : detectModuleDefaultExport(this.resourcePath);
68
+ return Promise.all([extractCss(this, cssOptions), defaultSignalPromise]).then(([css, defaultSignal]) => {
69
+ const emitDefault = shouldEmitCombinedDefault({
70
+ request,
71
+ skipSyntheticDefault,
72
+ detection: defaultSignal,
73
+ });
74
+ const stableSelectorsLiteral = typesRequested
75
+ ? buildStableSelectorsLiteral({
76
+ css,
77
+ namespace: resolvedNamespace,
78
+ resourcePath: this.resourcePath,
79
+ emitWarning: message => emitKnightedWarning(this, message),
80
+ })
81
+ : undefined;
82
+ return createCombinedModule(request, css, {
83
+ emitDefault,
84
+ stableSelectorsLiteral: stableSelectorsLiteral?.literal,
85
+ });
86
+ });
48
87
  };
49
88
  loader.pitch = pitch;
50
89
  export default loader;
51
90
  function resolveLoaderOptions(ctx) {
52
91
  const rawOptions = (typeof ctx.getOptions === 'function' ? ctx.getOptions() : {});
53
- const { vanilla, ...rest } = rawOptions;
92
+ const { vanilla, stableNamespace, ...rest } = rawOptions;
54
93
  const cssOptions = {
55
94
  ...rest,
56
95
  cwd: rest.cwd ?? ctx.rootContext ?? process.cwd(),
@@ -58,6 +97,7 @@ function resolveLoaderOptions(ctx) {
58
97
  return {
59
98
  cssOptions,
60
99
  vanillaOptions: vanilla,
100
+ stableNamespace,
61
101
  };
62
102
  }
63
103
  async function extractCss(ctx, options) {
@@ -71,19 +111,12 @@ async function extractCss(ctx, options) {
71
111
  function toSourceString(source) {
72
112
  return typeof source === 'string' ? source : source.toString('utf8');
73
113
  }
74
- function buildInjection(css) {
75
- return `\n\nexport const ${DEFAULT_EXPORT_NAME} = ${JSON.stringify(css)};\n`;
76
- }
77
- function hasCombinedQuery(query) {
78
- if (!query)
79
- return false;
80
- const trimmed = query.startsWith('?') ? query.slice(1) : query;
81
- if (!trimmed)
82
- return false;
83
- return trimmed
84
- .split('&')
85
- .filter(Boolean)
86
- .some(part => isQueryFlag(part, COMBINED_QUERY_FLAG));
114
+ function buildInjection(css, extras) {
115
+ const lines = [`\n\nexport const ${DEFAULT_EXPORT_NAME} = ${JSON.stringify(css)};\n`];
116
+ if (extras?.stableSelectorsLiteral) {
117
+ lines.push(extras.stableSelectorsLiteral);
118
+ }
119
+ return lines.join('');
87
120
  }
88
121
  function buildProxyRequest(ctx) {
89
122
  const sanitizedQuery = buildSanitizedQuery(ctx.resourceQuery);
@@ -111,51 +144,28 @@ function stripResourceQuery(request) {
111
144
  const idx = request.indexOf('?');
112
145
  return idx >= 0 ? request.slice(0, idx) : request;
113
146
  }
114
- function buildSanitizedQuery(query) {
115
- if (!query)
116
- return '';
117
- const entries = splitQuery(query).filter(part => {
118
- return !isQueryFlag(part, COMBINED_QUERY_FLAG) && !isQueryFlag(part, 'knighted-css');
119
- });
120
- return entries.length > 0 ? `?${entries.join('&')}` : '';
121
- }
122
- function splitQuery(query) {
123
- const trimmed = query.startsWith('?') ? query.slice(1) : query;
124
- if (!trimmed)
125
- return [];
126
- return trimmed.split('&').filter(Boolean);
127
- }
128
- function isQueryFlag(entry, flag) {
129
- const [rawKey] = entry.split('=');
130
- try {
131
- return decodeURIComponent(rawKey) === flag;
132
- }
133
- catch {
134
- return rawKey === flag;
135
- }
136
- }
137
- function createCombinedModule(request, css) {
147
+ function createCombinedModule(request, css, options) {
148
+ const shouldEmitDefault = options?.emitDefault ?? shouldForwardDefaultExport(request);
138
149
  const requestLiteral = JSON.stringify(request);
139
150
  const lines = [
140
151
  `import * as __knightedModule from ${requestLiteral};`,
141
152
  `export * from ${requestLiteral};`,
142
153
  ];
143
- if (shouldForwardDefaultExport(request)) {
154
+ if (shouldEmitDefault) {
144
155
  lines.push(`const __knightedDefault =
145
156
  typeof __knightedModule.default !== 'undefined'
146
157
  ? __knightedModule.default
147
158
  : __knightedModule;`, 'export default __knightedDefault;');
148
159
  }
149
- lines.push(buildInjection(css));
160
+ lines.push(buildInjection(css, { stableSelectorsLiteral: options?.stableSelectorsLiteral }));
150
161
  return lines.join('\n');
151
162
  }
152
- function shouldForwardDefaultExport(request) {
153
- const [pathPart] = request.split('?');
154
- if (!pathPart)
155
- return true;
156
- const lower = pathPart.toLowerCase();
157
- if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
158
- return false;
163
+ function emitKnightedWarning(ctx, message) {
164
+ const formatted = `\x1b[33m@knighted/css warning\x1b[0m ${message}`;
165
+ if (typeof ctx.emitWarning === 'function') {
166
+ ctx.emitWarning(new Error(formatted));
167
+ return;
159
168
  }
160
- return true;
169
+ // eslint-disable-next-line no-console
170
+ console.warn(formatted);
161
171
  }
@@ -0,0 +1,23 @@
1
+ import type { ModuleDefaultSignal } from './moduleInfo.js';
2
+ export declare const COMBINED_QUERY_FLAG = "combined";
3
+ export declare const TYPES_QUERY_FLAG = "types";
4
+ export declare const NAMED_ONLY_QUERY_FLAGS: readonly ["named-only", "no-default"];
5
+ export type SelectorTypeVariant = 'types' | 'combined' | 'combinedWithoutDefault';
6
+ export declare function splitQuery(query: string): string[];
7
+ export declare function isQueryFlag(entry: string, flag: string): boolean;
8
+ export declare function buildSanitizedQuery(query?: string | null): string;
9
+ export declare function hasQueryFlag(query: string | null | undefined, flag: string): boolean;
10
+ export declare function shouldForwardDefaultExport(request: string): boolean;
11
+ export declare function hasCombinedQuery(query?: string | null): boolean;
12
+ export declare function hasNamedOnlyQueryFlag(query?: string | null): boolean;
13
+ export declare function determineSelectorVariant(query?: string | null): SelectorTypeVariant;
14
+ export declare function shouldEmitCombinedDefault(options: {
15
+ detection: ModuleDefaultSignal;
16
+ request: string;
17
+ skipSyntheticDefault: boolean;
18
+ }): boolean;
19
+ export declare const __loaderInternals: {
20
+ buildSanitizedQuery: typeof buildSanitizedQuery;
21
+ shouldEmitCombinedDefault: typeof shouldEmitCombinedDefault;
22
+ determineSelectorVariant: typeof determineSelectorVariant;
23
+ };
@@ -0,0 +1,96 @@
1
+ export const COMBINED_QUERY_FLAG = 'combined';
2
+ export const TYPES_QUERY_FLAG = 'types';
3
+ export const NAMED_ONLY_QUERY_FLAGS = ['named-only', 'no-default'];
4
+ export function splitQuery(query) {
5
+ const trimmed = query.startsWith('?') ? query.slice(1) : query;
6
+ if (!trimmed)
7
+ return [];
8
+ return trimmed.split('&').filter(Boolean);
9
+ }
10
+ export function isQueryFlag(entry, flag) {
11
+ const [rawKey] = entry.split('=');
12
+ try {
13
+ return decodeURIComponent(rawKey) === flag;
14
+ }
15
+ catch {
16
+ return rawKey === flag;
17
+ }
18
+ }
19
+ export function buildSanitizedQuery(query) {
20
+ if (!query)
21
+ return '';
22
+ const entries = splitQuery(query).filter(part => {
23
+ if (isQueryFlag(part, COMBINED_QUERY_FLAG)) {
24
+ return false;
25
+ }
26
+ if (isQueryFlag(part, 'knighted-css')) {
27
+ return false;
28
+ }
29
+ if (isQueryFlag(part, TYPES_QUERY_FLAG)) {
30
+ return false;
31
+ }
32
+ if (NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag))) {
33
+ return false;
34
+ }
35
+ return true;
36
+ });
37
+ return entries.length > 0 ? `?${entries.join('&')}` : '';
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
+ }
55
+ export function shouldForwardDefaultExport(request) {
56
+ const [pathPart] = request.split('?');
57
+ if (!pathPart)
58
+ return true;
59
+ const lower = pathPart.toLowerCase();
60
+ if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
61
+ return false;
62
+ }
63
+ return true;
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
+ }
77
+ export function shouldEmitCombinedDefault(options) {
78
+ if (options.skipSyntheticDefault) {
79
+ return false;
80
+ }
81
+ if (!shouldForwardDefaultExport(options.request)) {
82
+ return false;
83
+ }
84
+ if (options.detection === 'has-default') {
85
+ return true;
86
+ }
87
+ if (options.detection === 'no-default') {
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ export const __loaderInternals = {
93
+ buildSanitizedQuery,
94
+ shouldEmitCombinedDefault,
95
+ determineSelectorVariant,
96
+ };
@@ -0,0 +1,15 @@
1
+ import type { CssResolver } from './types.js';
2
+ export interface ModuleGraphOptions {
3
+ tsConfig?: string | Record<string, unknown>;
4
+ extensions?: string[];
5
+ conditions?: string[];
6
+ }
7
+ interface CollectOptions {
8
+ cwd: string;
9
+ styleExtensions: string[];
10
+ filter: (filePath: string) => boolean;
11
+ resolver?: CssResolver;
12
+ graphOptions?: ModuleGraphOptions;
13
+ }
14
+ export declare function collectStyleImports(entryPath: string, options: CollectOptions): Promise<string[]>;
15
+ export {};