auklet 0.0.4 → 0.0.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
@@ -95,6 +95,24 @@ Each `styles.dependencies` entry may define:
95
95
  - `themes`: theme style dependency map.
96
96
  - `components`: glob-like component style rules used to infer style imports from source imports.
97
97
 
98
+ Component style inference only scans source `.tsx` files. Component imports or
99
+ re-exports in `.ts` files are ignored for CSS auto import, so component barrel
100
+ files that should drive component CSS must be `.tsx`.
101
+
102
+ Supported value forms include named imports, named re-exports, and local
103
+ re-exports that can be traced back to an import binding:
104
+
105
+ ```tsx
106
+ import { Button } from '@scope/ui';
107
+ export { Card } from '@scope/ui/components/Card';
108
+
109
+ import { Dialog as BaseDialog } from '@scope/ui';
110
+ export { BaseDialog as Dialog };
111
+ ```
112
+
113
+ `export * from '...'` is intentionally not supported for CSS auto import because
114
+ the exported component names cannot be inferred reliably.
115
+
98
116
  Style configuration should use the grouped `styles` field.
99
117
 
100
118
  ```ts
@@ -1,5 +1,5 @@
1
- import { createRequire } from 'node:module';
2
1
  import { fileURLToPath } from 'node:url';
2
+ import { createRequire } from 'node:module';
3
3
  import { execa } from 'execa';
4
4
  const require = createRequire(import.meta.url);
5
5
  const tsdownRunFile = require.resolve('tsdown/run');
@@ -1,4 +1,3 @@
1
- import { type UserConfig } from 'tsdown/config';
2
1
  import type { AukletConfig, PackageBuildFormat } from '#auklet/types';
3
2
  export type TsdownFormat = PackageBuildFormat;
4
3
  export declare function defineKernelPackageConfigFromOptions(
@@ -144,5 +143,74 @@ export declare function defineKernelPackageConfigFromFile(
144
143
  }
145
144
  )[]
146
145
  >;
147
- declare const _default: Promise<UserConfig[]>;
146
+ declare const _default: Promise<
147
+ (
148
+ | {
149
+ entry: {
150
+ index: string;
151
+ };
152
+ format: PackageBuildFormat;
153
+ globalName: string;
154
+ outDir: string;
155
+ dts: boolean;
156
+ treeshake: true;
157
+ banner: string;
158
+ outExtensions: () => {
159
+ js: string;
160
+ };
161
+ outputOptions: {
162
+ entryFileNames: string;
163
+ chunkFileNames: string;
164
+ globals: {
165
+ [k: string]: string;
166
+ };
167
+ };
168
+ cwd: string;
169
+ root: string;
170
+ clean: false;
171
+ sourcemap: false;
172
+ tsconfig: string;
173
+ target: NonNullable<
174
+ import('#auklet/types').PackageBuildTarget | undefined
175
+ >;
176
+ platform: NonNullable<
177
+ import('#auklet/types').PackageBuildPlatform | undefined
178
+ >;
179
+ deps: import('node_modules/tsdown/dist/types-CQaSBA5U.mjs').L;
180
+ define: {
181
+ __TEST__: string;
182
+ __VERSION__: string;
183
+ __DEV__: string;
184
+ };
185
+ }
186
+ | {
187
+ entry: Record<string, string>;
188
+ format: 'cjs' | 'esm';
189
+ outDir: string;
190
+ dts: true;
191
+ unbundle: true;
192
+ outExtensions: () => {
193
+ js: string;
194
+ dts: string;
195
+ };
196
+ cwd: string;
197
+ root: string;
198
+ clean: false;
199
+ sourcemap: false;
200
+ tsconfig: string;
201
+ target: NonNullable<
202
+ import('#auklet/types').PackageBuildTarget | undefined
203
+ >;
204
+ platform: NonNullable<
205
+ import('#auklet/types').PackageBuildPlatform | undefined
206
+ >;
207
+ deps: import('node_modules/tsdown/dist/types-CQaSBA5U.mjs').L;
208
+ define: {
209
+ __TEST__: string;
210
+ __VERSION__: string;
211
+ __DEV__: string;
212
+ };
213
+ }
214
+ )[]
215
+ >;
148
216
  export default _default;
@@ -1,6 +1,5 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { defineConfig } from 'tsdown/config';
4
3
  import { loadAukletConfig } from '#auklet/configLoader';
5
4
  import { normalizeAukletConfig } from '#auklet/config';
6
5
  const formatMap = {
@@ -263,6 +262,4 @@ export async function defineKernelPackageConfigFromFile(
263
262
  const config = await loadAukletConfig(packageRoot, { cacheBust: true });
264
263
  return defineKernelPackageConfigFromOptions(packageRoot, config);
265
264
  }
266
- export default defineKernelPackageConfigFromFile(process.cwd()).then((config) =>
267
- defineConfig(config),
268
- );
265
+ export default defineKernelPackageConfigFromFile(process.cwd());
@@ -1,4 +1,5 @@
1
1
  export declare const NODE_MODULES_DIR = 'node_modules';
2
+ export declare const SOURCE_COMPONENT_MODULE_RE: RegExp;
2
3
  export declare const THEMES_DIR = 'themes';
3
4
  export declare const THEMES_ENTRY_PREFIX = 'themes/';
4
5
  export declare const STYLE_ENTRY = 'style.css';
@@ -1,4 +1,5 @@
1
1
  export const NODE_MODULES_DIR = 'node_modules';
2
+ export const SOURCE_COMPONENT_MODULE_RE = /\.tsx$/;
2
3
  export const THEMES_DIR = 'themes';
3
4
  export const THEMES_ENTRY_PREFIX = 'themes/';
4
5
  export const STYLE_ENTRY = 'style.css';
@@ -7,6 +7,7 @@ import { StyleModuleEntryPlanner } from '#auklet/css/core/styleModuleEntryPlanne
7
7
  import {
8
8
  EXTERNAL_ENTRY,
9
9
  MODULE_ENTRY,
10
+ SOURCE_COMPONENT_MODULE_RE,
10
11
  STYLE_ENTRY,
11
12
  THEMES_ENTRY_PREFIX,
12
13
  } from '#auklet/css/constants';
@@ -60,7 +61,7 @@ export class ModuleStyleGraph {
60
61
  return false;
61
62
  }
62
63
  if (normalizedFile.endsWith(aukletConfigFile)) return true;
63
- if (normalizedFile.endsWith('.ts') || normalizedFile.endsWith('.tsx')) {
64
+ if (SOURCE_COMPONENT_MODULE_RE.test(normalizedFile)) {
64
65
  return true;
65
66
  }
66
67
  return this.config.styleExtensions.some((extension) =>
@@ -16,7 +16,6 @@ export declare class ModuleStyleImportCollector {
16
16
  files: Array<string>,
17
17
  config: NormalizedAukletConfig,
18
18
  ): Map<string, string[]>;
19
- private getImportDeclarations;
20
19
  private collectSourceImportStyle;
21
20
  private resolveSourceImportStyleEntry;
22
21
  private resolveSourceImportPath;
@@ -27,5 +26,4 @@ export declare class ModuleStyleImportCollector {
27
26
  private getImportPathValues;
28
27
  private createStyleSpecifier;
29
28
  private createDirectStyleSpecifier;
30
- private getImportedNames;
31
29
  }
@@ -1,15 +1,18 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import ts from 'typescript';
4
3
  import { isArray } from 'aidly';
4
+ import { SOURCE_COMPONENT_MODULE_RE } from '#auklet/css/constants';
5
5
  import { joinDependencySpecifier } from '#auklet/css/core/style/specifier';
6
6
  import {
7
7
  appendUniqueMapValue,
8
8
  getSourceModuleDir,
9
- SOURCE_DECLARATION_RE,
10
9
  POSIX_SEPARATOR,
11
- SOURCE_MODULE_RE,
12
10
  } from '#auklet/utils';
11
+ import {
12
+ collectModuleStyleSourceReferences,
13
+ getSourceReferenceImportedNames,
14
+ isTypeOnlySourceReference,
15
+ } from '#auklet/css/core/styleImports/sourceReference';
13
16
  const GLOBSTAR_TOKEN = '**';
14
17
  export class ModuleStyleImportCollector {
15
18
  srcRoot;
@@ -28,14 +31,14 @@ export class ModuleStyleImportCollector {
28
31
  const entries = new Map();
29
32
  const rules = this.createAutoImportRules(config);
30
33
  for (const file of files) {
31
- if (!SOURCE_MODULE_RE.test(file) || SOURCE_DECLARATION_RE.test(file)) {
34
+ if (!SOURCE_COMPONENT_MODULE_RE.test(file)) {
32
35
  continue;
33
36
  }
34
37
  const sourceRelative = path.relative(this.srcRoot, file);
35
38
  const sourceDir = path.dirname(sourceRelative);
36
39
  const sourceModuleDir = getSourceModuleDir(sourceRelative);
37
40
  const code = fs.readFileSync(file, 'utf8');
38
- const imports = this.getImportDeclarations(file, code);
41
+ const imports = collectModuleStyleSourceReferences(file, code);
39
42
  for (const item of imports) {
40
43
  this.collectSourceImportStyle(
41
44
  entries,
@@ -43,6 +46,7 @@ export class ModuleStyleImportCollector {
43
46
  sourceModuleDir,
44
47
  item,
45
48
  );
49
+ if (isTypeOnlySourceReference(item)) continue;
46
50
  const importPath = item.importPath;
47
51
  const ruleMatches = this.matchAutoImportRules(rules, importPath);
48
52
  if (!ruleMatches.length) continue;
@@ -63,7 +67,7 @@ export class ModuleStyleImportCollector {
63
67
  continue;
64
68
  }
65
69
  for (const ruleMatch of ruleMatches) {
66
- const importedNames = this.getImportedNames(file, item);
70
+ const importedNames = getSourceReferenceImportedNames(file, item);
67
71
  for (const importedName of importedNames) {
68
72
  const specifier = this.createStyleSpecifier(
69
73
  ruleMatch.rule,
@@ -81,31 +85,8 @@ export class ModuleStyleImportCollector {
81
85
  }
82
86
  return entries;
83
87
  }
84
- getImportDeclarations(file, code) {
85
- const imports = [];
86
- const sourceFile = ts.createSourceFile(
87
- file,
88
- code,
89
- ts.ScriptTarget.Latest,
90
- false,
91
- file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
92
- );
93
- sourceFile.forEachChild((node) => {
94
- if (
95
- !ts.isImportDeclaration(node) ||
96
- !ts.isStringLiteral(node.moduleSpecifier)
97
- ) {
98
- return;
99
- }
100
- imports.push({
101
- importPath: node.moduleSpecifier.text,
102
- importClause: node.importClause,
103
- });
104
- });
105
- return imports;
106
- }
107
88
  collectSourceImportStyle(entries, sourceDir, sourceModuleDir, item) {
108
- if (item.importClause?.isTypeOnly) return;
89
+ if (isTypeOnlySourceReference(item)) return;
109
90
  const importedStyleEntry = this.resolveSourceImportStyleEntry(
110
91
  sourceDir,
111
92
  item.importPath,
@@ -238,30 +219,4 @@ export class ModuleStyleImportCollector {
238
219
  }
239
220
  return `${importPath}${suffix}`;
240
221
  }
241
- getImportedNames(file, item) {
242
- const importClause = item.importClause;
243
- if (!importClause || importClause.isTypeOnly) {
244
- return [];
245
- }
246
- const names = [];
247
- if (importClause.name) {
248
- names.push(importClause.name.text);
249
- }
250
- const namedBindings = importClause.namedBindings;
251
- if (!namedBindings) {
252
- return names;
253
- }
254
- if (ts.isNamespaceImport(namedBindings)) {
255
- throw new Error(
256
- `Namespace import is not supported for CSS auto import: ${item.importPath}\n` +
257
- `Use named imports instead, for example: import { Component } from '${item.importPath}'.\n` +
258
- `File: ${file}`,
259
- );
260
- }
261
- for (const element of namedBindings.elements) {
262
- if (element.isTypeOnly) continue;
263
- names.push((element.propertyName ?? element.name).text);
264
- }
265
- return names;
266
- }
267
222
  }
@@ -0,0 +1,19 @@
1
+ import ts from 'typescript';
2
+ export type ModuleStyleSourceReference = {
3
+ importPath: string;
4
+ importClause?: ts.ImportClause;
5
+ importedNames?: Array<string>;
6
+ isTypeOnly?: boolean;
7
+ hasNamespaceImport?: boolean;
8
+ };
9
+ export declare function collectModuleStyleSourceReferences(
10
+ file: string,
11
+ code: string,
12
+ ): ModuleStyleSourceReference[];
13
+ export declare function isTypeOnlySourceReference(
14
+ item: ModuleStyleSourceReference,
15
+ ): boolean | undefined;
16
+ export declare function getSourceReferenceImportedNames(
17
+ file: string,
18
+ item: ModuleStyleSourceReference,
19
+ ): string[];
@@ -0,0 +1,193 @@
1
+ import ts from 'typescript';
2
+ export function collectModuleStyleSourceReferences(file, code) {
3
+ const imports = [];
4
+ const importBindings = new Map();
5
+ const localDeclarations = new Set();
6
+ const sourceFile = ts.createSourceFile(
7
+ file,
8
+ code,
9
+ ts.ScriptTarget.Latest,
10
+ false,
11
+ file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
12
+ );
13
+ sourceFile.forEachChild((node) => {
14
+ collectLocalDeclarationNames(node, localDeclarations);
15
+ if (ts.isImportDeclaration(node)) {
16
+ collectImportBindings(node, importBindings);
17
+ }
18
+ });
19
+ sourceFile.forEachChild((node) => {
20
+ if (ts.isImportDeclaration(node)) {
21
+ if (!ts.isStringLiteral(node.moduleSpecifier)) {
22
+ return;
23
+ }
24
+ imports.push({
25
+ importPath: node.moduleSpecifier.text,
26
+ importClause: node.importClause,
27
+ });
28
+ return;
29
+ }
30
+ if (ts.isExportDeclaration(node)) {
31
+ collectExportDeclaration(
32
+ file,
33
+ node,
34
+ imports,
35
+ importBindings,
36
+ localDeclarations,
37
+ );
38
+ }
39
+ });
40
+ return imports;
41
+ }
42
+ export function isTypeOnlySourceReference(item) {
43
+ return isTypeOnlyImportClause(item.importClause) || item.isTypeOnly;
44
+ }
45
+ export function getSourceReferenceImportedNames(file, item) {
46
+ if (item.importedNames) {
47
+ if (item.isTypeOnly) return [];
48
+ if (item.hasNamespaceImport) {
49
+ throw createNamespaceImportError(file, item.importPath);
50
+ }
51
+ return item.importedNames;
52
+ }
53
+ const importClause = item.importClause;
54
+ if (!importClause || isTypeOnlyImportClause(importClause)) {
55
+ return [];
56
+ }
57
+ const names = [];
58
+ if (importClause.name) {
59
+ names.push(importClause.name.text);
60
+ }
61
+ const namedBindings = importClause.namedBindings;
62
+ if (!namedBindings) {
63
+ return names;
64
+ }
65
+ if (ts.isNamespaceImport(namedBindings)) {
66
+ throw createNamespaceImportError(file, item.importPath);
67
+ }
68
+ for (const element of namedBindings.elements) {
69
+ if (element.isTypeOnly) continue;
70
+ names.push((element.propertyName ?? element.name).text);
71
+ }
72
+ return names;
73
+ }
74
+ const collectLocalDeclarationNames = (node, localDeclarations) => {
75
+ if (
76
+ (ts.isFunctionDeclaration(node) ||
77
+ ts.isClassDeclaration(node) ||
78
+ ts.isInterfaceDeclaration(node) ||
79
+ ts.isTypeAliasDeclaration(node) ||
80
+ ts.isEnumDeclaration(node)) &&
81
+ node.name
82
+ ) {
83
+ localDeclarations.add(node.name.text);
84
+ return;
85
+ }
86
+ if (!ts.isVariableStatement(node)) return;
87
+ for (const declaration of node.declarationList.declarations) {
88
+ if (ts.isIdentifier(declaration.name)) {
89
+ localDeclarations.add(declaration.name.text);
90
+ }
91
+ }
92
+ };
93
+ const collectImportBindings = (node, importBindings) => {
94
+ if (!ts.isStringLiteral(node.moduleSpecifier) || !node.importClause) {
95
+ return;
96
+ }
97
+ const importPath = node.moduleSpecifier.text;
98
+ const isTypeOnly = isTypeOnlyImportClause(node.importClause);
99
+ if (node.importClause.name) {
100
+ const importedName = node.importClause.name.text;
101
+ importBindings.set(importedName, {
102
+ importPath,
103
+ importedName,
104
+ isTypeOnly,
105
+ hasNamespaceImport: false,
106
+ });
107
+ }
108
+ const namedBindings = node.importClause.namedBindings;
109
+ if (!namedBindings) return;
110
+ if (ts.isNamespaceImport(namedBindings)) {
111
+ importBindings.set(namedBindings.name.text, {
112
+ importPath,
113
+ importedName: namedBindings.name.text,
114
+ isTypeOnly,
115
+ hasNamespaceImport: true,
116
+ });
117
+ return;
118
+ }
119
+ for (const element of namedBindings.elements) {
120
+ const importedName = (element.propertyName ?? element.name).text;
121
+ importBindings.set(element.name.text, {
122
+ importPath,
123
+ importedName,
124
+ isTypeOnly: isTypeOnly || element.isTypeOnly,
125
+ hasNamespaceImport: false,
126
+ });
127
+ }
128
+ };
129
+ const collectExportDeclaration = (
130
+ file,
131
+ node,
132
+ imports,
133
+ importBindings,
134
+ localDeclarations,
135
+ ) => {
136
+ if (node.moduleSpecifier) {
137
+ if (!ts.isStringLiteral(node.moduleSpecifier)) return;
138
+ if (!node.exportClause || !ts.isNamedExports(node.exportClause)) {
139
+ throw new Error(
140
+ `[auklet:css] Export-all declarations are not supported for CSS auto import: ${node.moduleSpecifier.text}\n` +
141
+ `Use named exports instead, for example: export { Component } from '${node.moduleSpecifier.text}'.\n` +
142
+ `File: ${file}`,
143
+ );
144
+ }
145
+ imports.push({
146
+ importPath: node.moduleSpecifier.text,
147
+ importedNames: getExportedNames(node),
148
+ isTypeOnly: node.isTypeOnly,
149
+ });
150
+ return;
151
+ }
152
+ if (!node.exportClause || !ts.isNamedExports(node.exportClause)) {
153
+ return;
154
+ }
155
+ for (const element of node.exportClause.elements) {
156
+ if (node.isTypeOnly || element.isTypeOnly) continue;
157
+ const localName = (element.propertyName ?? element.name).text;
158
+ const binding = importBindings.get(localName);
159
+ if (binding) {
160
+ imports.push({
161
+ importPath: binding.importPath,
162
+ importedNames: [binding.importedName],
163
+ isTypeOnly: binding.isTypeOnly,
164
+ hasNamespaceImport: binding.hasNamespaceImport,
165
+ });
166
+ continue;
167
+ }
168
+ if (!localDeclarations.has(localName)) {
169
+ throw new Error(
170
+ `[auklet:css] Unable to resolve exported symbol "${localName}" for CSS auto import.\n` +
171
+ `File: ${file}`,
172
+ );
173
+ }
174
+ }
175
+ };
176
+ const getExportedNames = (node) => {
177
+ if (!node.exportClause || !ts.isNamedExports(node.exportClause)) {
178
+ return [];
179
+ }
180
+ return node.exportClause.elements
181
+ .filter((element) => !element.isTypeOnly)
182
+ .map((element) => (element.propertyName ?? element.name).text);
183
+ };
184
+ const isTypeOnlyImportClause = (importClause) => {
185
+ return importClause?.phaseModifier === ts.SyntaxKind.TypeKeyword;
186
+ };
187
+ const createNamespaceImportError = (file, importPath) => {
188
+ return new Error(
189
+ `Namespace import is not supported for CSS auto import: ${importPath}\n` +
190
+ `Use named imports instead, for example: import { Component } from '${importPath}'.\n` +
191
+ `File: ${file}`,
192
+ );
193
+ };
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { SOURCE_COMPONENT_MODULE_RE } from '#auklet/css/constants';
2
3
  import { groupStyleFilesByDir } from '#auklet/css/core/style/files';
3
4
  import { getSourceModuleDir, toPosixPath } from '#auklet/utils';
4
5
  export class StyleModuleEntryPlanner {
@@ -39,7 +40,7 @@ export class StyleModuleEntryPlanner {
39
40
  }
40
41
  getSourceModuleDirs() {
41
42
  return this.packageContext.sourceFiles
42
- .filter((sourceFile) => sourceFile.endsWith('.tsx'))
43
+ .filter((sourceFile) => SOURCE_COMPONENT_MODULE_RE.test(sourceFile))
43
44
  .map((sourceFile) => {
44
45
  return getSourceModuleDir(
45
46
  path.relative(this.packageContext.sourceRoot, sourceFile),
@@ -1,4 +1,4 @@
1
- import { ModuleStyleImportCollector } from '#auklet/css/core/moduleStyleImportCollector';
1
+ import { ModuleStyleImportCollector } from '#auklet/css/core/styleImports/collector';
2
2
  import { StyleProcessor } from '#auklet/css/core/styleProcessor';
3
3
  import { WorkspaceStyleResolver } from '#auklet/css/core/workspaceStyleResolver';
4
4
  import type {
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { ModuleStyleImportCollector } from '#auklet/css/core/moduleStyleImportCollector';
3
+ import { ModuleStyleImportCollector } from '#auklet/css/core/styleImports/collector';
4
4
  import { StyleProcessor } from '#auklet/css/core/styleProcessor';
5
5
  import { WorkspaceStyleResolver } from '#auklet/css/core/workspaceStyleResolver';
6
6
  import {
@@ -18,5 +18,6 @@ export declare class ModuleStyleWatcher {
18
18
  private rebuild;
19
19
  private refreshWatcher;
20
20
  private scheduleBuild;
21
+ private shouldRebuildForFile;
21
22
  close(): Promise<void>;
22
23
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import chokidar from 'chokidar';
4
4
  import { aukletConfigFile, aukletDefaultOptions } from '#auklet/config';
5
5
  import { moduleStyleBuildConfig } from '#auklet/css/config';
6
+ import { SOURCE_COMPONENT_MODULE_RE } from '#auklet/css/constants';
6
7
  import { ModuleStyleBuilder } from '#auklet/css/production/builder';
7
8
  export class ModuleStyleWatcher {
8
9
  config;
@@ -59,7 +60,10 @@ export class ModuleStyleWatcher {
59
60
  interval: 300,
60
61
  usePolling: true,
61
62
  });
62
- this.watcher.on('all', () => {
63
+ this.watcher.on('all', (_event, file) => {
64
+ if (typeof file === 'string' && !this.shouldRebuildForFile(file)) {
65
+ return;
66
+ }
63
67
  this.scheduleBuild();
64
68
  });
65
69
  this.watcher.on('error', (error) => {
@@ -75,6 +79,11 @@ export class ModuleStyleWatcher {
75
79
  });
76
80
  }, 80);
77
81
  }
82
+ shouldRebuildForFile(file) {
83
+ if (path.basename(file) === aukletConfigFile) return true;
84
+ if (SOURCE_COMPONENT_MODULE_RE.test(file)) return true;
85
+ return this.config.styleExtensions.includes(path.extname(file));
86
+ }
78
87
  async close() {
79
88
  if (this.timer) clearTimeout(this.timer);
80
89
  await this.watcher?.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auklet",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "author": "chentao.arthur",
6
6
  "packageManager": "pnpm@10.27.0",