@nuee/cli 0.4.1 → 0.5.0

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/dist/registry.js CHANGED
@@ -1,81 +1,49 @@
1
- import { dependencyVersions, getRegistryItem } from "@nuee/registry";
2
- export async function resolveComponent(name) {
3
- if (URL.canParse(name))
4
- return resolveRemoteComponent(name);
5
- const components = new Set();
6
- const externalDependencies = new Set();
7
- const files = new Map();
8
- async function visit(componentName) {
9
- if (components.has(componentName))
10
- return;
11
- const item = await getRegistryItem(componentName);
12
- components.add(componentName);
13
- for (const file of item.files)
14
- files.set(file.path, file);
15
- for (const dependency of item.dependencies)
16
- externalDependencies.add(dependency);
17
- for (const dependency of item.registryDependencies) {
18
- await visit(dependency);
19
- }
1
+ import { dependencyVersions, getRegistryItem, parseRegistryItem, } from "@nuee/registry";
2
+ async function readRegistryItem(name) {
3
+ if (!URL.canParse(name))
4
+ return getRegistryItem(name);
5
+ const response = await fetch(name);
6
+ if (!response.ok)
7
+ throw new Error(`Could not load the registry: ${response.status}`);
8
+ const value = await response.json();
9
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
10
+ throw new Error("This is not a valid Nuee registry item.");
20
11
  }
21
- await visit(name);
22
- return {
23
- components: [...components],
24
- files: [...files.values()],
25
- externalDependencies: [...externalDependencies].map((dependency) => `${dependency}@${dependencyVersions[dependency] ?? "latest"}`),
26
- primaryExport: (await getRegistryItem(name)).primaryExport,
12
+ const candidate = value;
13
+ const item = {
14
+ ...candidate,
15
+ name: candidate.name === undefined ? name : candidate.name,
16
+ primaryExport: candidate.primaryExport === undefined
17
+ ? (candidate.name ?? "Component")
18
+ : candidate.primaryExport,
19
+ registryDependencies: candidate.registryDependencies === undefined ? [] : candidate.registryDependencies,
27
20
  };
21
+ return parseRegistryItem(item);
28
22
  }
29
- async function resolveRemoteComponent(url) {
23
+ export async function resolveComponent(name) {
24
+ const visited = new Set();
30
25
  const components = new Set();
31
- const externalDependencies = new Set();
26
+ const dependencies = new Set();
32
27
  const files = new Map();
33
- async function visitLocal(name) {
34
- if (components.has(name))
28
+ const root = await readRegistryItem(name);
29
+ async function visit(key, item) {
30
+ if (visited.has(key))
35
31
  return;
36
- const item = await getRegistryItem(name);
37
- components.add(name);
38
- for (const file of item.files)
32
+ visited.add(key);
33
+ const current = item ?? (await readRegistryItem(key));
34
+ components.add(current.name);
35
+ for (const file of current.files)
39
36
  files.set(file.path, file);
40
- for (const dependency of item.dependencies)
41
- externalDependencies.add(dependency);
42
- for (const dependency of item.registryDependencies)
43
- await visitLocal(dependency);
44
- }
45
- async function visitRemote(itemUrl) {
46
- const response = await fetch(itemUrl);
47
- if (!response.ok)
48
- throw new Error(`Could not load the registry: ${response.status}`);
49
- const item = (await response.json());
50
- if (!Array.isArray(item.files) ||
51
- !Array.isArray(item.dependencies) ||
52
- !item.files.every((file) => typeof file === "object" &&
53
- file !== null &&
54
- typeof file.path === "string" &&
55
- typeof file.content === "string")) {
56
- throw new Error("This is not a valid Nuee registry item.");
57
- }
58
- const itemName = item.name ?? itemUrl;
59
- if (components.has(itemName))
60
- return;
61
- components.add(itemName);
62
- for (const file of item.files)
63
- files.set(file.path, file);
64
- for (const dependency of item.dependencies)
65
- externalDependencies.add(dependency);
66
- for (const dependency of item.registryDependencies ?? []) {
67
- if (URL.canParse(dependency))
68
- await visitRemote(dependency);
69
- else
70
- await visitLocal(dependency);
71
- }
72
- return item.primaryExport ?? item.name ?? "Component";
37
+ for (const dependency of current.dependencies)
38
+ dependencies.add(dependency);
39
+ for (const dependency of current.registryDependencies)
40
+ await visit(dependency);
73
41
  }
74
- const primaryExport = (await visitRemote(url)) ?? "Component";
42
+ await visit(name, root);
75
43
  return {
76
44
  components: [...components],
77
- externalDependencies: [...externalDependencies].map((dependency) => `${dependency}@${dependencyVersions[dependency] ?? "latest"}`),
78
45
  files: [...files.values()],
79
- primaryExport,
46
+ externalDependencies: [...dependencies].map((dependency) => `${dependency}@${dependencyVersions[dependency] ?? "latest"}`),
47
+ primaryExport: root.primaryExport,
80
48
  };
81
49
  }
@@ -0,0 +1,14 @@
1
+ import { type ArrayExpression } from "@babel/types";
2
+ export declare function parseSource(source: string): import("@babel/parser").ParseResult<import("@babel/types").File>;
3
+ type StylexImport = {
4
+ name: string;
5
+ direct: boolean;
6
+ };
7
+ export declare function inspectViteConfig(source: string): {
8
+ plugins: ArrayExpression;
9
+ imports: StylexImport[];
10
+ hasCompiler: boolean;
11
+ } | undefined;
12
+ export declare function configureVite(source: string): string;
13
+ export declare function removeReducedMotionStyles(source: string): string;
14
+ export {};
package/dist/source.js ADDED
@@ -0,0 +1,162 @@
1
+ import { parse } from "@babel/parser";
2
+ import { traverseFast } from "@babel/types";
3
+ const parserOptions = { sourceType: "module", plugins: ["typescript", "jsx"] };
4
+ export function parseSource(source) {
5
+ return parse(source, { ...parserOptions, tokens: true, plugins: [...parserOptions.plugins] });
6
+ }
7
+ function getViteImports(body) {
8
+ const defineConfigNames = new Set();
9
+ const stylexImports = [];
10
+ for (const node of body) {
11
+ if (node.type !== "ImportDeclaration")
12
+ continue;
13
+ if (node.source.value === "vite") {
14
+ for (const specifier of node.specifiers) {
15
+ if (specifier.type !== "ImportSpecifier")
16
+ continue;
17
+ if (specifier.imported.type !== "Identifier")
18
+ continue;
19
+ if (specifier.imported.name !== "defineConfig")
20
+ continue;
21
+ defineConfigNames.add(specifier.local.name);
22
+ }
23
+ continue;
24
+ }
25
+ if (node.source.value !== "@stylexjs/unplugin" &&
26
+ node.source.value !== "@stylexjs/unplugin/vite")
27
+ continue;
28
+ for (const specifier of node.specifiers) {
29
+ if (specifier.type !== "ImportDefaultSpecifier")
30
+ continue;
31
+ stylexImports.push({
32
+ name: specifier.local.name,
33
+ direct: node.source.value.endsWith("/vite"),
34
+ });
35
+ }
36
+ }
37
+ return { defineConfigNames, stylexImports };
38
+ }
39
+ function hasStylexCompiler(plugins, imports) {
40
+ for (const node of plugins.elements) {
41
+ if (node?.type !== "CallExpression")
42
+ continue;
43
+ const callee = node.callee;
44
+ if (callee.type === "Identifier") {
45
+ if (imports.some((binding) => binding.direct && binding.name === callee.name))
46
+ return true;
47
+ continue;
48
+ }
49
+ if (callee.type !== "MemberExpression" || callee.computed)
50
+ continue;
51
+ if (callee.object.type !== "Identifier")
52
+ continue;
53
+ if (callee.property.type !== "Identifier" || callee.property.name !== "vite")
54
+ continue;
55
+ const name = callee.object.name;
56
+ if (imports.some((binding) => !binding.direct && binding.name === name))
57
+ return true;
58
+ }
59
+ return false;
60
+ }
61
+ export function inspectViteConfig(source) {
62
+ const file = parseSource(source);
63
+ const { defineConfigNames, stylexImports } = getViteImports(file.program.body);
64
+ const exported = file.program.body.find((node) => node.type === "ExportDefaultDeclaration");
65
+ if (!exported || exported.type !== "ExportDefaultDeclaration")
66
+ return undefined;
67
+ let config = exported.declaration;
68
+ if (config.type === "CallExpression" && config.arguments.length === 1) {
69
+ const { callee } = config;
70
+ if (callee.type !== "Identifier" || !defineConfigNames.has(callee.name))
71
+ return undefined;
72
+ const argument = config.arguments[0];
73
+ if (argument.type !== "ObjectExpression")
74
+ return undefined;
75
+ config = argument;
76
+ }
77
+ if (config.type !== "ObjectExpression")
78
+ return undefined;
79
+ let plugins;
80
+ for (const property of config.properties) {
81
+ if (property.type === "SpreadElement" || property.computed)
82
+ return undefined;
83
+ if (property.type !== "ObjectProperty")
84
+ continue;
85
+ const key = property.key;
86
+ const isPlugins = (key.type === "Identifier" && key.name === "plugins") ||
87
+ (key.type === "StringLiteral" && key.value === "plugins");
88
+ if (!isPlugins)
89
+ continue;
90
+ if (plugins || property.value.type !== "ArrayExpression")
91
+ return undefined;
92
+ plugins = property.value;
93
+ }
94
+ if (!plugins)
95
+ return undefined;
96
+ return {
97
+ plugins,
98
+ imports: stylexImports,
99
+ hasCompiler: hasStylexCompiler(plugins, stylexImports),
100
+ };
101
+ }
102
+ export function configureVite(source) {
103
+ const inspected = inspectViteConfig(source);
104
+ if (!inspected || inspected.plugins.elements.some((node) => node?.type === "SpreadElement")) {
105
+ throw new Error("Could not safely update the Vite plugins array. Add the StyleX compiler manually.");
106
+ }
107
+ if (inspected.hasCompiler)
108
+ return source;
109
+ const binding = inspected.imports[0];
110
+ // An unused name avoids colliding with a user variable or an existing import.
111
+ let name = "nueeStylex";
112
+ while (source.includes(name))
113
+ name += "_";
114
+ let factory = `${name}.vite`;
115
+ if (binding)
116
+ factory = binding.direct ? binding.name : `${binding.name}.vite`;
117
+ const plugin = `${factory}({ unstable_moduleResolution: { type: "commonJS" } })`;
118
+ const offset = inspected.plugins.start + 1;
119
+ const configured = source.slice(0, offset) +
120
+ plugin +
121
+ (inspected.plugins.elements.length ? ", " : "") +
122
+ source.slice(offset);
123
+ const result = binding ? configured : `import ${name} from "@stylexjs/unplugin";\n${configured}`;
124
+ parseSource(result);
125
+ return result;
126
+ }
127
+ export function removeReducedMotionStyles(source) {
128
+ const file = parseSource(source);
129
+ const removals = [];
130
+ traverseFast(file, (node) => {
131
+ if (node.type !== "ObjectProperty" ||
132
+ node.computed ||
133
+ node.key.type !== "StringLiteral" ||
134
+ node.key.value !== "@media (prefers-reduced-motion: reduce)")
135
+ return;
136
+ const start = node.start;
137
+ let end = node.end;
138
+ const nextToken = file.tokens?.find((token) => token.start >= end && typeof token.type !== "string");
139
+ if (nextToken && source.slice(nextToken.start, nextToken.end) === ",")
140
+ end = nextToken.end;
141
+ removals.push({ start, end });
142
+ });
143
+ // Keep outer ranges first so nested rules are removed with their parent.
144
+ const outerRemovals = [];
145
+ let coveredEnd = -1;
146
+ for (const removal of removals.sort((a, b) => a.start - b.start)) {
147
+ if (removal.end <= coveredEnd)
148
+ continue;
149
+ outerRemovals.push(removal);
150
+ coveredEnd = removal.end;
151
+ }
152
+ let result = source;
153
+ let boundary = source.length;
154
+ for (const removal of outerRemovals.reverse()) {
155
+ if (removal.end > boundary)
156
+ continue;
157
+ result = result.slice(0, removal.start) + result.slice(removal.end);
158
+ boundary = removal.start;
159
+ }
160
+ parseSource(result);
161
+ return result;
162
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuee/cli",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,11 +23,14 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
- "@nuee/registry": "0.4.1",
26
+ "@babel/parser": "7.29.8",
27
+ "@babel/types": "7.29.8",
28
+ "@nuee/registry": "0.5.0",
29
+ "get-tsconfig": "^4.14.3",
27
30
  "jsonc-parser": "^3.3.1"
28
31
  },
29
32
  "devDependencies": {
30
- "@types/node": "^22.20.1",
33
+ "@types/node": "26.4.1",
31
34
  "typescript": "7.0.2"
32
35
  },
33
36
  "scripts": {