@nuee/cli 0.4.0 → 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";
2
- import { join } from "node:path";
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
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
- import { getTokenFiles } from "@nuee/registry";
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,69 +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.");
34
+ return undefined;
35
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 addResetImport(projectDirectory);
54
- await writeNueeFiles(projectDirectory, tokenDirectory, refreshLegacyTokens);
55
- await writeFile(configPath, configuredVite, "utf8");
36
+ function getRelativeImportPath(from, to) {
37
+ const path = relative(dirname(from), to).split(sep).join("/");
38
+ return path.startsWith(".") ? path : `./${path}`;
56
39
  }
57
- async function addResetImport(projectDirectory) {
58
- const cssPath = join(projectDirectory, "src/index.css");
59
- const resetImport = '@import "@nuee/ui/reset.css";';
60
- try {
61
- const source = await readFile(cssPath, "utf8");
62
- if (source.includes(resetImport))
63
- return;
64
- await writeFile(cssPath, `${resetImport}\n\n${source}`, "utf8");
65
- }
66
- catch (error) {
67
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
68
- await writeFile(cssPath, `${resetImport}\n`, "utf8");
69
- return;
40
+ async function prepareFoundationFiles(tokenDirectory, refreshLegacyTokens) {
41
+ const files = [];
42
+ for (const file of await getFoundationFiles()) {
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" });
70
62
  }
71
- throw error;
72
- }
73
- }
74
- async function writeNueeFiles(projectDirectory, tokenDirectory, refreshLegacyTokens = false) {
75
- await mkdir(tokenDirectory, { recursive: true });
76
- for (const file of await getTokenFiles()) {
77
- await writeTokenFile(join(tokenDirectory, file.name), file.content, refreshLegacyTokens, file.name);
78
63
  }
64
+ return files;
79
65
  }
80
- function isLegacySemanticSource(fileName, source) {
81
- return (fileName === "semantic.stylex.ts" &&
82
- source.includes('bgCanvas: "initial"') &&
83
- source.includes('overlay: "initial"'));
84
- }
85
- 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;
86
70
  try {
87
- const currentSource = await readFile(path, "utf8");
88
- if (shouldRefreshLegacySources && isLegacySemanticSource(fileName, currentSource)) {
89
- await writeFile(path, source, "utf8");
90
- }
71
+ source = await readFile(path, "utf8");
91
72
  }
92
- catch {
93
- 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" };
94
80
  }
81
+ if (source.includes(resetImport))
82
+ return undefined;
83
+ return { path, source: `${resetImport}\n\n${source}`, flag: "w" };
95
84
  }
96
- export async function init(projectDirectory, options, shouldLog = true) {
85
+ export async function prepareInitialization(projectDirectory, options) {
97
86
  if ((await hasConfig(projectDirectory)) && !options.force) {
98
87
  throw new Error("nuee.json already exists. Use --force to create it again.");
99
88
  }
@@ -102,40 +91,62 @@ export async function init(projectDirectory, options, shouldLog = true) {
102
91
  ? createInterface({ input: process.stdin, output: process.stdout })
103
92
  : null;
104
93
  try {
105
- if (options.framework && options.framework !== "vite") {
106
- throw new Error(`Unsupported framework: ${options.framework}. Use vite or omit --framework.`);
107
- }
94
+ const defaultAliases = await getDefaultAliases(projectDirectory);
108
95
  const uiAlias = options.ui ??
109
96
  (readline
110
- ? await ask("Enter the UI import alias.", defaultConfig.aliases.ui, readline)
111
- : defaultConfig.aliases.ui);
97
+ ? await ask("Enter the UI import alias.", defaultAliases.ui, readline)
98
+ : defaultAliases.ui);
112
99
  const stylesAlias = options.styles ??
113
100
  options.tokens ??
114
101
  (readline
115
- ? await ask("Enter the styles import alias.", defaultConfig.aliases.styles, readline)
116
- : defaultConfig.aliases.styles);
102
+ ? await ask("Enter the styles import alias.", defaultAliases.styles, readline)
103
+ : defaultAliases.styles);
117
104
  const tokenDirectory = await resolveConfigAlias(projectDirectory, stylesAlias, "aliases.styles");
118
105
  await resolveConfigAlias(projectDirectory, uiAlias, "aliases.ui");
119
- if (!options["skip-dependencies"]) {
120
- await installDependencies(projectDirectory, ["@stylexjs/stylex"]);
121
- if (options.framework === "vite") {
122
- await installDependencies(projectDirectory, ["@nuee/ui", "@stylexjs/unplugin"], true);
123
- }
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.");
124
109
  }
125
- if (options.framework === "vite") {
126
- await writeViteFiles(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" });
127
117
  }
128
- else {
129
- await writeNueeFiles(projectDirectory, tokenDirectory, Boolean(options.force));
130
- }
131
- await writeConfig(projectDirectory, {
132
- accessibility: defaultConfig.accessibility,
133
- aliases: { ui: uiAlias, styles: stylesAlias },
134
- });
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
+ };
135
133
  }
136
134
  finally {
137
135
  readline?.close();
138
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);
139
150
  if (shouldLog)
140
151
  console.log("✔ Initialized Nuee.");
141
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
  }