@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/doctor.js CHANGED
@@ -1,22 +1,7 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
3
  import { hasConfig, readConfig, resolveConfigAlias } from "./config.js";
4
- async function readPackageJson(projectDirectory) {
5
- try {
6
- return JSON.parse(await readFile(resolve(projectDirectory, "package.json"), "utf8"));
7
- }
8
- catch {
9
- return undefined;
10
- }
11
- }
12
- async function readProjectFile(projectDirectory, fileName) {
13
- try {
14
- return await readFile(resolve(projectDirectory, fileName), "utf8");
15
- }
16
- catch {
17
- return undefined;
18
- }
19
- }
4
+ import { inspectViteConfig } from "./source.js";
20
5
  const entryFileNames = [
21
6
  "src/main.tsx",
22
7
  "src/main.jsx",
@@ -31,105 +16,132 @@ const entryFileNames = [
31
16
  "app/layout.tsx",
32
17
  "app/layout.jsx",
33
18
  ];
19
+ const viteConfigFileNames = [
20
+ "vite.config.ts",
21
+ "vite.config.mts",
22
+ "vite.config.js",
23
+ "vite.config.mjs",
24
+ ];
25
+ const tokenFileNames = ["color-palette.stylex.ts", "semantic.stylex.ts", "themes.stylex.ts"];
26
+ function isNotFoundError(error) {
27
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
28
+ }
29
+ async function readProjectFile(projectDirectory, fileName) {
30
+ try {
31
+ return await readFile(resolve(projectDirectory, fileName), "utf8");
32
+ }
33
+ catch (error) {
34
+ if (!isNotFoundError(error))
35
+ throw error;
36
+ return undefined;
37
+ }
38
+ }
39
+ async function readPackageJson(projectDirectory) {
40
+ const source = await readProjectFile(projectDirectory, "package.json");
41
+ return source === undefined ? undefined : JSON.parse(source);
42
+ }
34
43
  async function readEntrySources(projectDirectory) {
35
- const sources = await Promise.all(entryFileNames.map(async (fileName) => readProjectFile(projectDirectory, fileName)));
44
+ const sources = await Promise.all(entryFileNames.map((fileName) => readProjectFile(projectDirectory, fileName)));
36
45
  return sources.filter((source) => source !== undefined);
37
46
  }
38
47
  async function hasStylexCompiler(projectDirectory) {
39
- const configFileNames = [
40
- "vite.config.ts",
41
- "vite.config.mts",
42
- "vite.config.js",
43
- "vite.config.mjs",
44
- "next.config.ts",
45
- "next.config.mjs",
46
- "next.config.js",
47
- "webpack.config.ts",
48
- "webpack.config.mjs",
49
- "webpack.config.js",
50
- "rsbuild.config.ts",
51
- "rsbuild.config.mjs",
52
- "rsbuild.config.js",
53
- "rspack.config.ts",
54
- "rspack.config.mjs",
55
- "rspack.config.js",
56
- ];
57
- const sources = await Promise.all(configFileNames.map(async (fileName) => readProjectFile(projectDirectory, fileName)));
58
- return sources.some((source) => source?.includes("@stylexjs/unplugin") && /\b(?:stylex|unplugin)\.vite\(/.test(source));
48
+ const sources = await Promise.all(viteConfigFileNames.map((fileName) => readProjectFile(projectDirectory, fileName)));
49
+ return sources.some((source) => source !== undefined && inspectViteConfig(source)?.hasCompiler);
50
+ }
51
+ async function hasTokenFiles(tokenDirectory) {
52
+ for (const fileName of tokenFileNames) {
53
+ try {
54
+ await access(resolve(tokenDirectory, fileName));
55
+ }
56
+ catch (error) {
57
+ if (!isNotFoundError(error))
58
+ throw error;
59
+ return false;
60
+ }
61
+ }
62
+ return true;
59
63
  }
60
64
  function hasDependency(packageJson, dependency) {
61
65
  return Boolean(packageJson?.dependencies?.[dependency] ?? packageJson?.devDependencies?.[dependency]);
62
66
  }
63
- export async function doctor(projectDirectory) {
64
- const checks = [];
65
- const packageJson = await readPackageJson(projectDirectory);
66
- const entrySources = await readEntrySources(projectDirectory);
67
+ async function inspectLocalConfiguration(projectDirectory) {
67
68
  if (!(await hasConfig(projectDirectory))) {
68
- checks.push({
69
- detail: "Run `nuee init` to configure UI and styles aliases.",
70
- name: "nuee.json",
71
- status: "warn",
72
- });
69
+ return [
70
+ {
71
+ detail: "Run `nuee init` to configure UI and styles aliases.",
72
+ name: "nuee.json",
73
+ status: "warn",
74
+ },
75
+ ];
73
76
  }
74
- else {
75
- const config = await readConfig(projectDirectory);
76
- try {
77
- const uiDirectory = await resolveConfigAlias(projectDirectory, config.aliases.ui, "aliases.ui");
78
- const tokenDirectory = await resolveConfigAlias(projectDirectory, config.aliases.styles, "aliases.styles");
79
- checks.push({
77
+ const config = await readConfig(projectDirectory);
78
+ try {
79
+ const uiDirectory = await resolveConfigAlias(projectDirectory, config.aliases.ui, "aliases.ui");
80
+ const tokenDirectory = await resolveConfigAlias(projectDirectory, config.aliases.styles, "aliases.styles");
81
+ const tokenCheck = (await hasTokenFiles(tokenDirectory))
82
+ ? {
83
+ detail: `${config.aliases.styles} contains local token sources.`,
84
+ name: "Local tokens",
85
+ status: "pass",
86
+ }
87
+ : {
88
+ detail: `Create local token sources in ${config.aliases.styles} with \`nuee init --force\`.`,
89
+ name: "Local tokens",
90
+ status: "warn",
91
+ };
92
+ return [
93
+ {
80
94
  detail: `${config.aliases.ui} → ${uiDirectory}`,
81
95
  name: "UI path",
82
96
  status: "pass",
83
- });
84
- const tokenFiles = ["color-palette.stylex.ts", "semantic.stylex.ts", "themes.stylex.ts"];
85
- const hasTokenFiles = await Promise.all(tokenFiles.map(async (fileName) => {
86
- try {
87
- await access(resolve(tokenDirectory, fileName));
88
- return true;
89
- }
90
- catch {
91
- return false;
92
- }
93
- }));
94
- checks.push(hasTokenFiles.every(Boolean)
95
- ? {
96
- detail: `${config.aliases.styles} contains local token sources.`,
97
- name: "Local tokens",
98
- status: "pass",
99
- }
100
- : {
101
- detail: `Create local token sources in ${config.aliases.styles} with \`nuee init --force\`.`,
102
- name: "Local tokens",
103
- status: "warn",
104
- });
105
- }
106
- catch (error) {
107
- checks.push({
97
+ },
98
+ tokenCheck,
99
+ ];
100
+ }
101
+ catch (error) {
102
+ return [
103
+ {
108
104
  detail: error instanceof Error ? error.message : "Could not resolve nuee aliases.",
109
105
  name: "Aliases",
110
106
  status: "warn",
111
- });
112
- }
107
+ },
108
+ ];
113
109
  }
114
- checks.push(hasDependency(packageJson, "@stylexjs/stylex")
115
- ? { detail: "@stylexjs/stylex is installed.", name: "StyleX runtime", status: "pass" }
116
- : {
110
+ }
111
+ export async function doctor(projectDirectory) {
112
+ const packageJson = await readPackageJson(projectDirectory);
113
+ const entrySources = await readEntrySources(projectDirectory);
114
+ const checks = await inspectLocalConfiguration(projectDirectory);
115
+ if (hasDependency(packageJson, "@stylexjs/stylex")) {
116
+ checks.push({
117
+ detail: "@stylexjs/stylex is installed.",
118
+ name: "StyleX runtime",
119
+ status: "pass",
120
+ });
121
+ }
122
+ else {
123
+ checks.push({
117
124
  detail: "It is installed automatically by the first `nuee add`. Use --skip-dependencies to skip installation.",
118
125
  name: "StyleX runtime",
119
126
  status: "warn",
120
127
  });
121
- checks.push((await hasStylexCompiler(projectDirectory))
122
- ? {
123
- detail: "A supported config enables the StyleX compiler.",
128
+ }
129
+ if (await hasStylexCompiler(projectDirectory)) {
130
+ checks.push({
131
+ detail: "A Vite plugins array enables the StyleX compiler.",
124
132
  name: "StyleX compiler",
125
133
  status: "pass",
126
- }
127
- : {
128
- detail: "Configure the StyleX compiler for your bundler. Vite users can run `nuee init --framework vite`.",
134
+ });
135
+ }
136
+ else {
137
+ checks.push({
138
+ detail: "Could not verify a Vite StyleX compiler. Other bundlers require manual verification. Vite users can run `nuee init --vite`.",
129
139
  name: "StyleX compiler",
130
140
  status: "warn",
131
141
  });
132
- checks.push(entrySources.some((source) => /import\s+["'][^"']+\.css["']/.test(source))
142
+ }
143
+ const hasCssEntry = entrySources.some((source) => /import\s+["'][^"']+\.css["']/.test(source));
144
+ checks.push(hasCssEntry
133
145
  ? {
134
146
  detail: "An application entry imports a CSS entry point for StyleX output.",
135
147
  name: "CSS entry point",
@@ -140,17 +152,20 @@ export async function doctor(projectDirectory) {
140
152
  name: "CSS entry point",
141
153
  status: "warn",
142
154
  });
143
- checks.push(hasDependency(packageJson, "@stylexjs/unplugin")
144
- ? {
155
+ if (hasDependency(packageJson, "@stylexjs/unplugin")) {
156
+ checks.push({
145
157
  detail: "@stylexjs/unplugin is installed.",
146
158
  name: "StyleX build plugin",
147
159
  status: "pass",
148
- }
149
- : {
160
+ });
161
+ }
162
+ else {
163
+ checks.push({
150
164
  detail: "Add @stylexjs/unplugin to your bundler configuration, such as Vite or esbuild.",
151
165
  name: "StyleX build plugin",
152
166
  status: "warn",
153
167
  });
168
+ }
154
169
  for (const check of checks) {
155
170
  console.log(`${check.status === "pass" ? "✓" : "!"} ${check.name}: ${check.detail}`);
156
171
  }
package/dist/index.js CHANGED
@@ -1,43 +1,43 @@
1
1
  import { resolve } from "node:path";
2
2
  import { add } from "./add.js";
3
3
  import { parseArguments } from "./arguments.js";
4
- import { doctor } from "./doctor.js";
5
4
  import { docs, list } from "./docs.js";
5
+ import { doctor } from "./doctor.js";
6
6
  import { init } from "./init.js";
7
7
  import { newComponent } from "./new-component.js";
8
8
  export async function run(arguments_ = process.argv.slice(2)) {
9
- const { command, options, positionals } = parseArguments(arguments_);
10
- const projectDirectory = resolve(String(options.cwd ?? process.cwd()));
11
9
  try {
12
- if (command === "init") {
13
- await init(projectDirectory, options);
14
- }
15
- else if (command === "add") {
16
- await add(projectDirectory, positionals, options);
17
- }
18
- else if (command === "doctor") {
19
- await doctor(projectDirectory);
20
- }
21
- else if (command === "docs") {
22
- docs(positionals[0]);
23
- }
24
- else if (command === "list") {
25
- list();
26
- }
27
- else if (command === "new") {
28
- await newComponent(projectDirectory, positionals[0]);
29
- }
30
- else {
31
- console.log(`Nuee CLI
10
+ const { command, options, positionals } = parseArguments(arguments_);
11
+ const projectDirectory = resolve(String(options.cwd ?? process.cwd()));
12
+ switch (command) {
13
+ case "init":
14
+ await init(projectDirectory, options);
15
+ break;
16
+ case "add":
17
+ await add(projectDirectory, positionals, options);
18
+ break;
19
+ case "doctor":
20
+ return await doctor(projectDirectory);
21
+ case "docs":
22
+ docs(positionals[0]);
23
+ break;
24
+ case "list":
25
+ list();
26
+ break;
27
+ case "new":
28
+ await newComponent(projectDirectory, positionals[0]);
29
+ break;
30
+ default:
31
+ console.log(`Nuee CLI
32
32
 
33
33
  Usage:
34
- nuee init [--ui <path>] [--tokens <path>]
35
- nuee init --framework vite
34
+ nuee init [--ui <path>] [--styles <path>] [--vite]
36
35
  nuee add <component...> [--skip-dependencies] [--dry-run]
37
36
  nuee doctor
38
37
  nuee list
39
38
  nuee docs [component]
40
39
  nuee new <kebab-case-name>`);
40
+ break;
41
41
  }
42
42
  return true;
43
43
  }
package/dist/init.d.ts CHANGED
@@ -1,2 +1,23 @@
1
1
  import type { CliOptions } from "./arguments.js";
2
+ type PlannedFile = {
3
+ path: string;
4
+ source: string;
5
+ flag: "w" | "wx";
6
+ };
7
+ export declare function prepareInitialization(projectDirectory: string, options: CliOptions): Promise<{
8
+ files: PlannedFile[];
9
+ runtimeDependencies: readonly string[];
10
+ buildDependencies: readonly string[];
11
+ config: {
12
+ accessibility: {
13
+ respectReducedMotion: boolean;
14
+ };
15
+ aliases: {
16
+ ui: string;
17
+ styles: string;
18
+ };
19
+ };
20
+ }>;
21
+ export declare function applyInitialization(projectDirectory: string, plan: Awaited<ReturnType<typeof prepareInitialization>>): Promise<void>;
2
22
  export declare function init(projectDirectory: string, options: CliOptions, shouldLog?: boolean): Promise<void>;
23
+ export {};
package/dist/init.js CHANGED
@@ -1,14 +1,15 @@
1
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join, relative, sep } from "node:path";
3
3
  import { createInterface } from "node:readline/promises";
4
- import { defaultConfig, hasConfig, resolveConfigAlias, writeConfig } from "./config.js";
5
- import { installDependencies } from "./dependencies.js";
6
4
  import { getFoundationFiles } from "@nuee/registry";
5
+ import { defaultConfig, getDefaultAliases, hasConfig, resolveConfigAlias, writeConfig, } from "./config.js";
6
+ import { getMissingDependencies, installDependencies } from "./dependencies.js";
7
+ import { configureVite } from "./source.js";
7
8
  async function ask(question, defaultValue, readline) {
8
9
  const answer = await readline.question(`${question} (${defaultValue}) `);
9
10
  return answer.trim() || defaultValue;
10
11
  }
11
- async function readViteConfig(projectDirectory) {
12
+ async function findViteConfig(projectDirectory) {
12
13
  const configFileNames = [
13
14
  "vite.config.ts",
14
15
  "vite.config.mts",
@@ -18,7 +19,6 @@ async function readViteConfig(projectDirectory) {
18
19
  for (const fileName of configFileNames) {
19
20
  const path = join(projectDirectory, fileName);
20
21
  try {
21
- await access(path);
22
22
  return { path, source: await readFile(path, "utf8") };
23
23
  }
24
24
  catch (error) {
@@ -31,73 +31,58 @@ async function readViteConfig(projectDirectory) {
31
31
  throw error;
32
32
  }
33
33
  }
34
- throw new Error("Could not find a Vite config file.");
35
- }
36
- function configureVite(source) {
37
- const importLine = 'import stylex from "@stylexjs/unplugin";';
38
- const pluginPattern = /plugins:\s*\[([^\]]*)\]/s;
39
- const stylexPlugin = 'stylex.vite({ unstable_moduleResolution: { type: "commonJS" } })';
40
- if (!source.includes("stylex.vite(") && !pluginPattern.test(source)) {
41
- throw new Error("Could not safely update the Vite plugins array. Add stylex.vite() manually.");
42
- }
43
- if (source.includes("stylex.vite("))
44
- return source;
45
- return (source.includes(importLine) ? source : `${importLine}\n${source}`).replace(pluginPattern, (_, plugins) => {
46
- const prefix = plugins.trim() ? `${stylexPlugin}, ${plugins}` : stylexPlugin;
47
- return `plugins: [${prefix}]`;
48
- });
49
- }
50
- async function writeViteFiles(projectDirectory, tokenDirectory, refreshLegacyTokens) {
51
- const { path: configPath, source: configSource } = await readViteConfig(projectDirectory);
52
- const configuredVite = configureVite(configSource);
53
- await writeNueeFiles(projectDirectory, tokenDirectory, refreshLegacyTokens);
54
- await addResetImport(projectDirectory, tokenDirectory);
55
- await writeFile(configPath, configuredVite, "utf8");
34
+ return undefined;
56
35
  }
57
36
  function getRelativeImportPath(from, to) {
58
37
  const path = relative(dirname(from), to).split(sep).join("/");
59
38
  return path.startsWith(".") ? path : `./${path}`;
60
39
  }
61
- async function addResetImport(projectDirectory, tokenDirectory) {
62
- const cssPath = join(projectDirectory, "src/index.css");
63
- const resetImport = `@import "${getRelativeImportPath(cssPath, join(tokenDirectory, "reset.css"))}";`;
64
- try {
65
- const source = await readFile(cssPath, "utf8");
66
- if (source.includes(resetImport))
67
- return;
68
- await writeFile(cssPath, `${resetImport}\n\n${source}`, "utf8");
69
- }
70
- catch (error) {
71
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
72
- await writeFile(cssPath, `${resetImport}\n`, "utf8");
73
- return;
74
- }
75
- throw error;
76
- }
77
- }
78
- async function writeNueeFiles(projectDirectory, tokenDirectory, refreshLegacyTokens = false) {
79
- await mkdir(tokenDirectory, { recursive: true });
40
+ async function prepareFoundationFiles(tokenDirectory, refreshLegacyTokens) {
41
+ const files = [];
80
42
  for (const file of await getFoundationFiles()) {
81
- await writeTokenFile(join(tokenDirectory, file.name), file.content, refreshLegacyTokens, file.name);
43
+ const path = join(tokenDirectory, file.name);
44
+ let currentSource;
45
+ try {
46
+ currentSource = await readFile(path, "utf8");
47
+ }
48
+ catch (error) {
49
+ if (typeof error !== "object" ||
50
+ error === null ||
51
+ !("code" in error) ||
52
+ error.code !== "ENOENT")
53
+ throw error;
54
+ files.push({ path, source: file.content, flag: "wx" });
55
+ continue;
56
+ }
57
+ if (refreshLegacyTokens &&
58
+ file.name === "semantic.stylex.ts" &&
59
+ currentSource.includes('bgCanvas: "initial"') &&
60
+ currentSource.includes('overlay: "initial"')) {
61
+ files.push({ path, source: file.content, flag: "w" });
62
+ }
82
63
  }
64
+ return files;
83
65
  }
84
- function isLegacySemanticSource(fileName, source) {
85
- return (fileName === "semantic.stylex.ts" &&
86
- source.includes('bgCanvas: "initial"') &&
87
- source.includes('overlay: "initial"'));
88
- }
89
- async function writeTokenFile(path, source, shouldRefreshLegacySources, fileName) {
66
+ async function prepareResetImport(projectDirectory, tokenDirectory) {
67
+ const path = join(projectDirectory, "src/index.css");
68
+ const resetImport = `@import "${getRelativeImportPath(path, join(tokenDirectory, "reset.css"))}";`;
69
+ let source;
90
70
  try {
91
- const currentSource = await readFile(path, "utf8");
92
- if (shouldRefreshLegacySources && isLegacySemanticSource(fileName, currentSource)) {
93
- await writeFile(path, source, "utf8");
94
- }
71
+ source = await readFile(path, "utf8");
95
72
  }
96
- catch {
97
- await writeFile(path, source, "utf8");
73
+ catch (error) {
74
+ if (typeof error !== "object" ||
75
+ error === null ||
76
+ !("code" in error) ||
77
+ error.code !== "ENOENT")
78
+ throw error;
79
+ return { path, source: `${resetImport}\n`, flag: "wx" };
98
80
  }
81
+ if (source.includes(resetImport))
82
+ return undefined;
83
+ return { path, source: `${resetImport}\n\n${source}`, flag: "w" };
99
84
  }
100
- export async function init(projectDirectory, options, shouldLog = true) {
85
+ export async function prepareInitialization(projectDirectory, options) {
101
86
  if ((await hasConfig(projectDirectory)) && !options.force) {
102
87
  throw new Error("nuee.json already exists. Use --force to create it again.");
103
88
  }
@@ -106,40 +91,62 @@ export async function init(projectDirectory, options, shouldLog = true) {
106
91
  ? createInterface({ input: process.stdin, output: process.stdout })
107
92
  : null;
108
93
  try {
109
- if (options.framework && options.framework !== "vite") {
110
- throw new Error(`Unsupported framework: ${options.framework}. Use vite or omit --framework.`);
111
- }
94
+ const defaultAliases = await getDefaultAliases(projectDirectory);
112
95
  const uiAlias = options.ui ??
113
96
  (readline
114
- ? await ask("Enter the UI import alias.", defaultConfig.aliases.ui, readline)
115
- : defaultConfig.aliases.ui);
97
+ ? await ask("Enter the UI import alias.", defaultAliases.ui, readline)
98
+ : defaultAliases.ui);
116
99
  const stylesAlias = options.styles ??
117
100
  options.tokens ??
118
101
  (readline
119
- ? await ask("Enter the styles import alias.", defaultConfig.aliases.styles, readline)
120
- : defaultConfig.aliases.styles);
102
+ ? await ask("Enter the styles import alias.", defaultAliases.styles, readline)
103
+ : defaultAliases.styles);
121
104
  const tokenDirectory = await resolveConfigAlias(projectDirectory, stylesAlias, "aliases.styles");
122
105
  await resolveConfigAlias(projectDirectory, uiAlias, "aliases.ui");
123
- if (!options["skip-dependencies"]) {
124
- await installDependencies(projectDirectory, ["@stylexjs/stylex"]);
125
- if (options.framework === "vite") {
126
- await installDependencies(projectDirectory, ["@stylexjs/unplugin"], true);
127
- }
128
- }
129
- if (options.framework === "vite") {
130
- await writeViteFiles(projectDirectory, tokenDirectory, Boolean(options.force));
106
+ const viteConfig = options.vite ? await findViteConfig(projectDirectory) : undefined;
107
+ if (options.vite && !viteConfig) {
108
+ throw new Error("Could not find a Vite config file.");
131
109
  }
132
- else {
133
- await writeNueeFiles(projectDirectory, tokenDirectory, Boolean(options.force));
110
+ const configuredVite = viteConfig ? configureVite(viteConfig.source) : undefined;
111
+ const files = await prepareFoundationFiles(tokenDirectory, Boolean(options.force));
112
+ if (viteConfig && configuredVite !== undefined) {
113
+ const resetFile = await prepareResetImport(projectDirectory, tokenDirectory);
114
+ if (resetFile)
115
+ files.push(resetFile);
116
+ files.push({ path: viteConfig.path, source: configuredVite, flag: "w" });
134
117
  }
135
- await writeConfig(projectDirectory, {
136
- accessibility: defaultConfig.accessibility,
137
- aliases: { ui: uiAlias, styles: stylesAlias },
138
- });
118
+ const runtimeDependencies = await getMissingDependencies(projectDirectory, [
119
+ "@stylexjs/stylex",
120
+ ]);
121
+ const buildDependencies = await getMissingDependencies(projectDirectory, [
122
+ "@stylexjs/unplugin",
123
+ ]);
124
+ return {
125
+ files,
126
+ runtimeDependencies: options["skip-dependencies"] ? [] : runtimeDependencies,
127
+ buildDependencies: options["skip-dependencies"] || !options.vite ? [] : buildDependencies,
128
+ config: {
129
+ accessibility: defaultConfig.accessibility,
130
+ aliases: { ui: uiAlias, styles: stylesAlias },
131
+ },
132
+ };
139
133
  }
140
134
  finally {
141
135
  readline?.close();
142
136
  }
137
+ }
138
+ export async function applyInitialization(projectDirectory, plan) {
139
+ await installDependencies(projectDirectory, plan.runtimeDependencies);
140
+ await installDependencies(projectDirectory, plan.buildDependencies, true);
141
+ for (const file of plan.files) {
142
+ await mkdir(dirname(file.path), { recursive: true });
143
+ await writeFile(file.path, file.source, { encoding: "utf8", flag: file.flag });
144
+ }
145
+ await writeConfig(projectDirectory, plan.config);
146
+ }
147
+ export async function init(projectDirectory, options, shouldLog = true) {
148
+ const plan = await prepareInitialization(projectDirectory, options);
149
+ await applyInitialization(projectDirectory, plan);
143
150
  if (shouldLog)
144
151
  console.log("✔ Initialized Nuee.");
145
152
  }
@@ -1,10 +1,9 @@
1
- import { access, mkdir, writeFile } from "node:fs/promises";
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { readConfig, resolveConfigAlias } from "./config.js";
4
4
  function toPascalCase(name) {
5
5
  return name
6
6
  .split("-")
7
- .filter(Boolean)
8
7
  .map((part) => `${part[0]?.toUpperCase()}${part.slice(1)}`)
9
8
  .join("");
10
9
  }
@@ -17,19 +16,34 @@ export async function newComponent(projectDirectory, name) {
17
16
  const componentName = toPascalCase(name);
18
17
  const componentPath = join(uiDirectory, `${name}.tsx`);
19
18
  const tokenImport = `${config.aliases.styles}/semantic.stylex`;
20
- const files = [componentPath];
21
- for (const file of files) {
22
- try {
23
- await access(file);
24
- throw new Error(`${file} already exists. Existing components will not be overwritten.`);
25
- }
26
- catch (error) {
27
- if (!(error instanceof Error) || !error.message.includes("already exists"))
28
- continue;
29
- throw error;
19
+ await mkdir(uiDirectory, { recursive: true });
20
+ try {
21
+ await writeFile(componentPath, `import { colorVars, radiusVars, spacingVars } from "${tokenImport}";
22
+ import * as stylex from "@stylexjs/stylex";
23
+ import type { ComponentProps } from "react";
24
+
25
+ const styles = stylex.create({
26
+ root: {
27
+ backgroundColor: colorVars.bgSurface,
28
+ borderRadius: radiusVars.sm,
29
+ padding: spacingVars.space3,
30
+ },
31
+ });
32
+
33
+ export type ${componentName}Props = Omit<ComponentProps<"div">, "className" | "style"> & {
34
+ xstyle?: stylex.StyleXStyles;
35
+ };
36
+
37
+ export function ${componentName}({ xstyle, ...props }: ${componentName}Props) {
38
+ return <div {...props} {...stylex.props(styles.root, xstyle)} />;
39
+ }
40
+ `, { flag: "wx" });
41
+ }
42
+ catch (error) {
43
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST") {
44
+ throw new Error(`${componentPath} already exists. Existing components will not be overwritten.`);
30
45
  }
46
+ throw error;
31
47
  }
32
- await mkdir(uiDirectory, { recursive: true });
33
- await writeFile(componentPath, `import { colorVars, radiusVars, spacingVars } from "${tokenImport}";\nimport * as stylex from "@stylexjs/stylex";\nimport type { ComponentProps } from "react";\n\nconst styles = stylex.create({\n root: {\n backgroundColor: colorVars.bgSurface,\n borderRadius: radiusVars.sm,\n padding: spacingVars.space3,\n },\n});\n\nexport type ${componentName}Props = ComponentProps<"div"> & {\n xstyle?: stylex.StyleXStyles;\n};\n\nexport function ${componentName}({ className, style, xstyle, ...props }: ${componentName}Props) {\n const stylexProps = stylex.props(styles.root, xstyle);\n\n return (\n <div\n {...props}\n className={[stylexProps.className, className].filter(Boolean).join(" ")}\n style={{ ...stylexProps.style, ...style }}\n />\n );\n}\n`, { flag: "wx" });
34
48
  console.log(`Created a ${name} component scaffold: ${componentPath}`);
35
49
  }