@nooeh/cli 0.1.0 → 0.2.1

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/add.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
2
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { createInterface } from "node:readline/promises";
4
- import { defaultConfig, hasConfig, readConfig, resolveAliasPath, resolveTokensPath, } from "./config.js";
4
+ import { defaultConfig, hasConfig, readConfig, resolveConfigAlias } from "./config.js";
5
5
  import { installDependencies } from "./dependencies.js";
6
6
  import { init } from "./init.js";
7
7
  import { resolveComponent } from "./registry.js";
@@ -49,14 +49,8 @@ function resolveTargetPath(uiDirectory, filePath) {
49
49
  }
50
50
  return targetPath;
51
51
  }
52
- function replaceTokenImport(source, targetPath, tokenDirectory) {
53
- const tokenModulePath = join(tokenDirectory, "tokens.stylex.ts");
54
- const relativePath = relative(dirname(targetPath), tokenModulePath)
55
- .replace(/\.ts$/, "")
56
- .split(sep)
57
- .join("/");
58
- const tokenModule = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
59
- return source.replaceAll("@nooeh/tokens/tokens.stylex", tokenModule);
52
+ function replaceTokenImport(source, stylesAlias) {
53
+ return source.replaceAll("@nooeh/tokens/semantic.stylex", `${stylesAlias}/semantic.stylex`);
60
54
  }
61
55
  export async function add(projectDirectory, componentName, options = {}) {
62
56
  if (!componentName)
@@ -70,8 +64,7 @@ export async function add(projectDirectory, componentName, options = {}) {
70
64
  }
71
65
  const config = options["dry-run"] && shouldInitialize ? defaultConfig : await readConfig(projectDirectory);
72
66
  const resolved = await resolveComponent(componentName);
73
- const uiDirectory = await resolveAliasPath(projectDirectory, config.aliases.ui);
74
- const tokenDirectory = resolveTokensPath(projectDirectory, config.tokens);
67
+ const uiDirectory = await resolveConfigAlias(projectDirectory, config.aliases.ui, "aliases.ui");
75
68
  let isOverwriteConfirmed = false;
76
69
  async function confirmOverwrite() {
77
70
  if (isOverwriteConfirmed)
@@ -85,7 +78,7 @@ export async function add(projectDirectory, componentName, options = {}) {
85
78
  console.log(`Will add: ${targetPath}`);
86
79
  continue;
87
80
  }
88
- await writeSource(replaceTokenImport(file.content, targetPath, tokenDirectory), targetPath, confirmOverwrite);
81
+ await writeSource(replaceTokenImport(file.content, config.aliases.styles), targetPath, confirmOverwrite);
89
82
  }
90
83
  const shouldInstallDependencies = resolved.externalDependencies.length > 0 &&
91
84
  !options.skipDependencyInstall &&
@@ -101,5 +94,6 @@ export async function add(projectDirectory, componentName, options = {}) {
101
94
  return;
102
95
  }
103
96
  console.log(`Added ${componentName}.`);
104
- console.log(`import { ${primaryExport} } from "${config.aliases.ui}/${componentName}"`);
97
+ console.log(`Export: ${primaryExport}`);
98
+ console.log(`Location: ${relative(projectDirectory, join(uiDirectory, `${componentName}.tsx`))}`);
105
99
  }
@@ -5,8 +5,9 @@ export type CliOptions = Record<string, boolean | string | undefined> & {
5
5
  "dry-run"?: boolean;
6
6
  force?: boolean;
7
7
  "skip-dependencies"?: boolean;
8
+ styles?: string;
8
9
  tokens?: string;
9
- "ui-alias"?: string;
10
+ ui?: string;
10
11
  };
11
12
  export declare function parseArguments(arguments_: readonly string[]): {
12
13
  command: string;
package/dist/config.d.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  export declare const configFileName = "nooeh.json";
2
- export declare const configVersion = 2;
3
2
  export type NooehConfig = {
4
3
  aliases: {
5
4
  ui: string;
5
+ styles: string;
6
6
  };
7
- tokens: string;
8
- version?: number;
9
7
  };
10
8
  export declare const defaultConfig: NooehConfig;
11
9
  export declare function hasConfig(projectDirectory: string): Promise<boolean>;
12
10
  export declare function validateConfig(config: unknown): NooehConfig;
13
11
  export declare function readConfig(projectDirectory: string): Promise<NooehConfig>;
14
12
  export declare function writeConfig(projectDirectory: string, config: NooehConfig): Promise<string>;
15
- export declare function resolveTokensPath(projectDirectory: string, tokens: string): string;
16
- export declare function resolveAliasPath(projectDirectory: string, alias: string): Promise<string>;
13
+ export declare function resolveConfigPath(projectDirectory: string, path: string, name: string): string;
14
+ export declare function resolveConfigAlias(projectDirectory: string, alias: string, name: string): Promise<string>;
package/dist/config.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { access, readFile, writeFile } from "node:fs/promises";
2
- import { isAbsolute, relative, resolve } from "node:path";
2
+ import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  export const configFileName = "nooeh.json";
4
- export const configVersion = 2;
5
4
  export const defaultConfig = {
6
- aliases: { ui: "@/components/ui" },
7
- tokens: "src/styles/nooeh",
8
- version: configVersion,
5
+ aliases: {
6
+ ui: "@/components/ui",
7
+ styles: "@/styles",
8
+ },
9
9
  };
10
10
  export async function hasConfig(projectDirectory) {
11
11
  try {
@@ -25,22 +25,24 @@ function ensureRelativePath(projectDirectory, path, name) {
25
25
  }
26
26
  export function validateConfig(config) {
27
27
  const candidate = config;
28
- if (!candidate.aliases || typeof candidate.aliases.ui !== "string" || !candidate.aliases.ui) {
29
- throw new Error("Configure aliases.ui.");
28
+ if (candidate.paths || candidate.tokens) {
29
+ throw new Error("This nooeh.json uses an older format. Run `nooeh init --force` to create alias-based configuration.");
30
30
  }
31
- if (candidate.version !== undefined && candidate.version !== configVersion) {
32
- throw new Error(`Unsupported nooeh.json version: ${String(candidate.version)}`);
31
+ if (!candidate.aliases || typeof candidate.aliases !== "object") {
32
+ throw new Error("Configure aliases.");
33
33
  }
34
- if (typeof candidate.tokens !== "string" || !candidate.tokens.trim()) {
35
- throw new Error("Configure tokens.");
34
+ const aliases = candidate.aliases;
35
+ if (typeof aliases.ui !== "string" || !aliases.ui.trim()) {
36
+ throw new Error("Configure aliases.ui.");
36
37
  }
37
- if (isAbsolute(candidate.tokens)) {
38
- throw new Error("tokens must be inside the project directory.");
38
+ if (typeof aliases.styles !== "string" || !aliases.styles.trim()) {
39
+ throw new Error("Configure aliases.styles.");
39
40
  }
40
41
  return {
41
- aliases: { ui: candidate.aliases.ui },
42
- tokens: candidate.tokens,
43
- version: configVersion,
42
+ aliases: {
43
+ ui: aliases.ui,
44
+ styles: aliases.styles,
45
+ },
44
46
  };
45
47
  }
46
48
  function isNotFoundError(error) {
@@ -61,54 +63,54 @@ export async function readConfig(projectDirectory) {
61
63
  }
62
64
  export async function writeConfig(projectDirectory, config) {
63
65
  const validatedConfig = validateConfig(config);
64
- ensureRelativePath(projectDirectory, validatedConfig.tokens, "tokens");
65
66
  const configPath = resolve(projectDirectory, configFileName);
66
67
  await writeFile(configPath, `${JSON.stringify(validatedConfig, null, 2)}\n`, "utf8");
67
68
  return configPath;
68
69
  }
69
- export function resolveTokensPath(projectDirectory, tokens) {
70
- ensureRelativePath(projectDirectory, tokens, "tokens");
71
- return resolve(projectDirectory, tokens);
70
+ export function resolveConfigPath(projectDirectory, path, name) {
71
+ ensureRelativePath(projectDirectory, path, name);
72
+ return resolve(projectDirectory, path);
72
73
  }
73
- function findAliasTarget(alias, paths) {
74
- for (const [pattern, targets] of Object.entries(paths ?? {})) {
75
- const target = targets[0];
76
- if (!target)
77
- continue;
78
- const starIndex = pattern.indexOf("*");
79
- if (starIndex === -1) {
80
- if (alias === pattern)
81
- return target;
74
+ async function readTsConfig(projectDirectory) {
75
+ for (const fileName of ["tsconfig.json", "jsconfig.json"]) {
76
+ try {
77
+ return JSON.parse(await readFile(resolve(projectDirectory, fileName), "utf8"));
78
+ }
79
+ catch (error) {
80
+ if (!isNotFoundError(error)) {
81
+ continue;
82
+ }
83
+ }
84
+ }
85
+ return undefined;
86
+ }
87
+ function resolvePathAlias(alias, paths) {
88
+ for (const [pattern, targets] of Object.entries(paths)) {
89
+ const wildcardIndex = pattern.indexOf("*");
90
+ if (wildcardIndex === -1) {
91
+ if (pattern === alias)
92
+ return targets[0];
82
93
  continue;
83
94
  }
84
- const prefix = pattern.slice(0, starIndex);
85
- const suffix = pattern.slice(starIndex + 1);
95
+ const prefix = pattern.slice(0, wildcardIndex);
96
+ const suffix = pattern.slice(wildcardIndex + 1);
86
97
  if (!alias.startsWith(prefix) || !alias.endsWith(suffix))
87
98
  continue;
88
- const value = alias.slice(prefix.length, alias.length - suffix.length);
89
- return target.replaceAll("*", value);
99
+ const wildcard = alias.slice(prefix.length, alias.length - suffix.length);
100
+ return targets[0]?.replace("*", wildcard);
90
101
  }
91
102
  return undefined;
92
103
  }
93
- export async function resolveAliasPath(projectDirectory, alias) {
94
- for (const fileName of ["tsconfig.json", "jsconfig.json"]) {
95
- const configPath = resolve(projectDirectory, fileName);
96
- try {
97
- const projectConfig = JSON.parse(await readFile(configPath, "utf8"));
98
- const compilerOptions = projectConfig.compilerOptions ?? {};
99
- const aliasTarget = findAliasTarget(alias, compilerOptions.paths);
100
- if (!aliasTarget)
101
- continue;
102
- const baseDirectory = resolve(projectDirectory, compilerOptions.baseUrl ?? ".");
103
- const resolvedPath = resolve(baseDirectory, aliasTarget);
104
- ensureRelativePath(projectDirectory, resolvedPath, "UI");
105
- return resolvedPath;
106
- }
107
- catch (error) {
108
- if (isNotFoundError(error))
109
- continue;
110
- throw error;
111
- }
104
+ export async function resolveConfigAlias(projectDirectory, alias, name) {
105
+ if (alias.startsWith("@/")) {
106
+ return resolveConfigPath(projectDirectory, join("src", alias.slice(2)), name);
107
+ }
108
+ const tsConfig = await readTsConfig(projectDirectory);
109
+ const target = tsConfig?.compilerOptions?.paths
110
+ ? resolvePathAlias(alias, tsConfig.compilerOptions.paths)
111
+ : undefined;
112
+ if (!target) {
113
+ throw new Error(`Could not resolve ${name} (${alias}). Add it to tsconfig.json or jsconfig.json compilerOptions.paths.`);
112
114
  }
113
- throw new Error(`Could not find the ${alias} alias in compilerOptions.paths of tsconfig.json or jsconfig.json.`);
115
+ return resolveConfigPath(projectDirectory, join(tsConfig?.compilerOptions?.baseUrl ?? ".", target), name);
114
116
  }
package/dist/doctor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
- import { hasConfig, readConfig, resolveAliasPath, resolveTokensPath } from "./config.js";
3
+ import { hasConfig, readConfig, resolveConfigAlias } from "./config.js";
4
4
  async function readPackageJson(projectDirectory) {
5
5
  try {
6
6
  return JSON.parse(await readFile(resolve(projectDirectory, "package.json"), "utf8"));
@@ -55,7 +55,7 @@ async function hasStylexCompiler(projectDirectory) {
55
55
  "rspack.config.js",
56
56
  ];
57
57
  const sources = await Promise.all(configFileNames.map(async (fileName) => readProjectFile(projectDirectory, fileName)));
58
- return sources.some((source) => source?.includes("@stylexjs/unplugin") && source.includes("stylex("));
58
+ return sources.some((source) => source?.includes("@stylexjs/unplugin") && source.includes("stylex.vite("));
59
59
  }
60
60
  function hasDependency(packageJson, dependency) {
61
61
  return Boolean(packageJson?.dependencies?.[dependency] ?? packageJson?.devDependencies?.[dependency]);
@@ -66,46 +66,50 @@ export async function doctor(projectDirectory) {
66
66
  const entrySources = await readEntrySources(projectDirectory);
67
67
  if (!(await hasConfig(projectDirectory))) {
68
68
  checks.push({
69
- detail: "Run `nooeh init` to configure the default UI alias.",
69
+ detail: "Run `nooeh init` to configure UI and styles aliases.",
70
70
  name: "nooeh.json",
71
71
  status: "warn",
72
72
  });
73
73
  }
74
74
  else {
75
75
  const config = await readConfig(projectDirectory);
76
- const uiDirectory = await resolveAliasPath(projectDirectory, config.aliases.ui);
77
- checks.push({
78
- detail: `${config.aliases.ui} → ${uiDirectory}`,
79
- name: "UI alias",
80
- status: "pass",
81
- });
82
- const tokenDirectory = resolveTokensPath(projectDirectory, config.tokens);
83
- const tokenFiles = [
84
- "color-palette.stylex.ts",
85
- "tokens.stylex.ts",
86
- "themes.stylex.ts",
87
- "theme.ts",
88
- ];
89
- const hasTokenFiles = await Promise.all(tokenFiles.map(async (fileName) => {
90
- try {
91
- await access(resolve(tokenDirectory, fileName));
92
- return true;
93
- }
94
- catch {
95
- return false;
96
- }
97
- }));
98
- checks.push(hasTokenFiles.every(Boolean)
99
- ? {
100
- detail: `${config.tokens} contains local token sources.`,
101
- name: "Local tokens",
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({
80
+ detail: `${config.aliases.ui} → ${uiDirectory}`,
81
+ name: "UI path",
102
82
  status: "pass",
103
- }
104
- : {
105
- detail: `Create local token sources in ${config.tokens} with \`nooeh init --force\`.`,
106
- name: "Local tokens",
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 \`nooeh init --force\`.`,
102
+ name: "Local tokens",
103
+ status: "warn",
104
+ });
105
+ }
106
+ catch (error) {
107
+ checks.push({
108
+ detail: error instanceof Error ? error.message : "Could not resolve nooeh aliases.",
109
+ name: "Aliases",
107
110
  status: "warn",
108
111
  });
112
+ }
109
113
  }
110
114
  checks.push(hasDependency(packageJson, "@stylexjs/stylex")
111
115
  ? { detail: "@stylexjs/stylex is installed.", name: "StyleX runtime", status: "pass" }
@@ -125,26 +129,15 @@ export async function doctor(projectDirectory) {
125
129
  name: "StyleX compiler",
126
130
  status: "warn",
127
131
  });
128
- checks.push(entrySources.some((source) => source.includes("./styles/nooeh.css") || source.includes("@nooeh/ui/global.css"))
129
- ? {
130
- detail: "An application entry imports nooeh global CSS.",
131
- name: "Global CSS",
132
- status: "pass",
133
- }
134
- : {
135
- detail: "Import `src/styles/nooeh.css` from an application entry point.",
136
- name: "Global CSS",
137
- status: "warn",
138
- });
139
- checks.push(entrySources.some((source) => source.includes("applyNooehTheme()"))
132
+ checks.push(entrySources.some((source) => /import\s+["'][^"']+\.css["']/.test(source))
140
133
  ? {
141
- detail: "An application entry applies a nooeh theme.",
142
- name: "Theme application",
134
+ detail: "An application entry imports a CSS entry point for StyleX output.",
135
+ name: "CSS entry point",
143
136
  status: "pass",
144
137
  }
145
138
  : {
146
- detail: "Call applyNooehTheme() before rendering your application.",
147
- name: "Theme application",
139
+ detail: "Import an application CSS file from an entry point so StyleX can emit CSS.",
140
+ name: "CSS entry point",
148
141
  status: "warn",
149
142
  });
150
143
  checks.push(hasDependency(packageJson, "@stylexjs/unplugin")
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ export async function run(arguments_ = process.argv.slice(2)) {
31
31
  console.log(`Nooeh CLI
32
32
 
33
33
  Usage:
34
- nooeh init
34
+ nooeh init [--ui <path>] [--tokens <path>]
35
35
  nooeh init --framework vite
36
36
  nooeh add <component> [--skip-dependencies] [--dry-run]
37
37
  nooeh doctor
package/dist/init.js CHANGED
@@ -1,16 +1,13 @@
1
1
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { dirname, join, relative, sep } from "node:path";
2
+ import { join, relative } from "node:path";
3
3
  import { createInterface } from "node:readline/promises";
4
- import { configFileName, defaultConfig, hasConfig, resolveTokensPath, writeConfig, } from "./config.js";
4
+ import { configFileName, defaultConfig, hasConfig, resolveConfigAlias, writeConfig, } from "./config.js";
5
5
  import { installDependencies } from "./dependencies.js";
6
6
  import { getTokenFiles } from "@nooeh/registry";
7
7
  async function ask(question, defaultValue, readline) {
8
8
  const answer = await readline.question(`${question} (${defaultValue}) `);
9
9
  return answer.trim() || defaultValue;
10
10
  }
11
- function getSourceDirectory(projectDirectory) {
12
- return join(projectDirectory, "src");
13
- }
14
11
  async function readViteConfig(projectDirectory) {
15
12
  const configFileNames = [
16
13
  "vite.config.ts",
@@ -37,60 +34,39 @@ async function readViteConfig(projectDirectory) {
37
34
  throw new Error("Could not find a Vite config file.");
38
35
  }
39
36
  function configureVite(source) {
40
- const importLine = 'import stylex from "@stylexjs/unplugin/vite";';
37
+ const importLine = 'import stylex from "@stylexjs/unplugin";';
41
38
  const pluginPattern = /plugins:\s*\[([^\]]*)\]/s;
42
- if (source.includes("stylex("))
43
- return source;
44
- if (!pluginPattern.test(source)) {
45
- throw new Error("Could not safely update the Vite plugins array. Add stylex({ useCSSLayers: true }) manually.");
39
+ const configPattern = /defineConfig\(\{\s*/;
40
+ if (!source.includes("stylex.vite(") && !pluginPattern.test(source)) {
41
+ throw new Error("Could not safely update the Vite plugins array. Add stylex.vite({ useCSSLayers: true }) manually.");
46
42
  }
47
- const withImport = source.includes(importLine) ? source : `${importLine}\n${source}`;
48
- return withImport.replace(pluginPattern, (_, plugins) => {
49
- const prefix = plugins.trim()
50
- ? `stylex({ useCSSLayers: true }), ${plugins}`
51
- : "stylex({ useCSSLayers: true })";
52
- return `plugins: [${prefix}]`;
53
- });
54
- }
55
- function toModuleSpecifier(fromDirectory, path) {
56
- const source = relative(fromDirectory, path).replace(/\.ts$/, "").split(sep).join("/");
57
- return source.startsWith(".") ? source : `./${source}`;
43
+ const withStylex = source.includes("stylex.vite(")
44
+ ? source
45
+ : (source.includes(importLine) ? source : `${importLine}\n${source}`).replace(pluginPattern, (_, plugins) => {
46
+ const prefix = plugins.trim()
47
+ ? `stylex.vite({ useCSSLayers: true }), ${plugins}`
48
+ : "stylex.vite({ useCSSLayers: true })";
49
+ return `plugins: [${prefix}]`;
50
+ });
51
+ if (withStylex.includes("alias:"))
52
+ return withStylex;
53
+ if (!configPattern.test(withStylex)) {
54
+ throw new Error('Could not safely add the "@" Vite alias. Add resolve.alias["@"] manually.');
55
+ }
56
+ return withStylex.replace(configPattern, 'defineConfig({\n resolve: { alias: { "@": new URL("./src", import.meta.url).pathname } },\n ');
58
57
  }
59
58
  async function writeViteFiles(projectDirectory, tokenDirectory) {
60
59
  const { path: configPath, source: configSource } = await readViteConfig(projectDirectory);
61
60
  const configuredVite = configureVite(configSource);
62
- const { entryPath, stylePath, themePath } = getNooehPaths(projectDirectory, tokenDirectory);
63
- const entrySource = await readFile(entryPath, "utf8");
64
- const styleImport = 'import "./styles/nooeh.css";';
65
- const themeImport = `import { applyNooehTheme } from "${toModuleSpecifier(dirname(entryPath), themePath)}";`;
66
- const themeApply = "applyNooehTheme();";
67
- const imports = [styleImport, themeImport].filter((line) => !entrySource.includes(line));
68
- const nextEntrySource = `${imports.join("\n")}\n${entrySource}`;
69
61
  await writeNooehFiles(projectDirectory, tokenDirectory);
70
- await writeFile(entryPath, nextEntrySource.includes(themeApply)
71
- ? nextEntrySource
72
- : `${imports.length ? `${imports.join("\n")}\n` : ""}${themeApply}\n${entrySource}`, "utf8");
73
62
  await writeFile(configPath, configuredVite, "utf8");
74
- console.log(`Configured Vite and created ${relative(projectDirectory, stylePath)}.`);
75
- }
76
- function getNooehPaths(projectDirectory, tokenDirectory) {
77
- const sourceDirectory = getSourceDirectory(projectDirectory);
78
- return {
79
- entryPath: join(sourceDirectory, "main.tsx"),
80
- stylePath: join(sourceDirectory, "styles", "nooeh.css"),
81
- themePath: join(tokenDirectory, "theme.ts"),
82
- };
63
+ console.log(`Configured Vite and created ${relative(projectDirectory, tokenDirectory)}.`);
83
64
  }
84
65
  async function writeNooehFiles(projectDirectory, tokenDirectory) {
85
- const { entryPath, stylePath, themePath } = getNooehPaths(projectDirectory, tokenDirectory);
86
- await mkdir(dirname(stylePath), { recursive: true });
87
66
  await mkdir(tokenDirectory, { recursive: true });
88
- await writeFileIfMissing(stylePath, '@import "@nooeh/ui/global.css";\n');
89
67
  for (const file of await getTokenFiles()) {
90
68
  await writeFileIfMissing(join(tokenDirectory, file.name), file.content);
91
69
  }
92
- await writeFileIfMissing(themePath, `import { darkColorTheme, darkShadowTheme, lightColorTheme, lightShadowTheme } from "./themes.stylex";\nimport * as stylex from "@stylexjs/stylex";\n\nexport type NooehColorMode = "light" | "dark";\n\nconst themeClassNames = [\n stylex.props(lightColorTheme, lightShadowTheme).className,\n stylex.props(darkColorTheme, darkShadowTheme).className,\n]\n .filter(Boolean)\n .flatMap((className) => className.split(" "));\n\nexport function applyNooehTheme(mode: NooehColorMode = "light") {\n const colorTheme = mode === "dark" ? darkColorTheme : lightColorTheme;\n const shadowTheme = mode === "dark" ? darkShadowTheme : lightShadowTheme;\n const themeClassName = stylex.props(colorTheme, shadowTheme).className ?? "";\n const root = document.documentElement;\n\n root.dataset.theme = mode;\n root.classList.remove(...themeClassNames);\n root.classList.add(...themeClassName.split(" ").filter(Boolean));\n}\n`);
93
- return { entryPath, stylePath, themePath };
94
70
  }
95
71
  async function writeFileIfMissing(path, source) {
96
72
  try {
@@ -112,28 +88,32 @@ export async function init(projectDirectory, options) {
112
88
  if (options.framework && options.framework !== "vite") {
113
89
  throw new Error(`Unsupported framework: ${options.framework}. Use vite or omit --framework.`);
114
90
  }
115
- const uiAlias = options["ui-alias"] ??
91
+ const uiAlias = options.ui ??
116
92
  (readline
117
- ? await ask("Enter the UI alias.", defaultConfig.aliases.ui, readline)
93
+ ? await ask("Enter the UI import alias.", defaultConfig.aliases.ui, readline)
118
94
  : defaultConfig.aliases.ui);
119
- const tokens = options.tokens ??
95
+ const stylesAlias = options.styles ??
96
+ options.tokens ??
120
97
  (readline
121
- ? await ask("Enter the token directory.", defaultConfig.tokens, readline)
122
- : defaultConfig.tokens);
123
- const tokenDirectory = resolveTokensPath(projectDirectory, tokens);
98
+ ? await ask("Enter the styles import alias.", defaultConfig.aliases.styles, readline)
99
+ : defaultConfig.aliases.styles);
100
+ const tokenDirectory = await resolveConfigAlias(projectDirectory, stylesAlias, "aliases.styles");
101
+ await resolveConfigAlias(projectDirectory, uiAlias, "aliases.ui");
124
102
  if (!options["skip-dependencies"]) {
125
103
  await installDependencies(projectDirectory, ["@stylexjs/stylex"]);
126
- await installDependencies(projectDirectory, ["@stylexjs/unplugin"], true);
104
+ if (options.framework === "vite") {
105
+ await installDependencies(projectDirectory, ["@stylexjs/unplugin"], true);
106
+ }
127
107
  }
128
108
  if (options.framework === "vite") {
129
109
  await writeViteFiles(projectDirectory, tokenDirectory);
130
110
  }
131
111
  else {
132
- const { stylePath, themePath } = await writeNooehFiles(projectDirectory, tokenDirectory);
133
- console.log(`Created ${relative(projectDirectory, stylePath)} and ${relative(projectDirectory, themePath)}.`);
134
- console.log("Import the CSS and call applyNooehTheme() from your application entry point.");
112
+ await writeNooehFiles(projectDirectory, tokenDirectory);
113
+ console.log(`Created ${relative(projectDirectory, tokenDirectory)}.`);
114
+ console.log("Configure the StyleX compiler for your bundler before importing added components.");
135
115
  }
136
- await writeConfig(projectDirectory, { aliases: { ui: uiAlias }, tokens });
116
+ await writeConfig(projectDirectory, { aliases: { ui: uiAlias, styles: stylesAlias } });
137
117
  }
138
118
  finally {
139
119
  readline?.close();
@@ -1,6 +1,6 @@
1
1
  import { access, mkdir, writeFile } from "node:fs/promises";
2
- import { dirname, join, relative, sep } from "node:path";
3
- import { readConfig, resolveAliasPath, resolveTokensPath } from "./config.js";
2
+ import { join } from "node:path";
3
+ import { readConfig, resolveConfigAlias } from "./config.js";
4
4
  function toPascalCase(name) {
5
5
  return name
6
6
  .split("-")
@@ -13,17 +13,10 @@ export async function newComponent(projectDirectory, name) {
13
13
  throw new Error("Enter a component name in kebab-case. For example: status-chip.");
14
14
  }
15
15
  const config = await readConfig(projectDirectory);
16
- const uiDirectory = await resolveAliasPath(projectDirectory, config.aliases.ui);
16
+ const uiDirectory = await resolveConfigAlias(projectDirectory, config.aliases.ui, "aliases.ui");
17
17
  const componentName = toPascalCase(name);
18
18
  const componentPath = join(uiDirectory, `${name}.tsx`);
19
- const tokenPath = join(resolveTokensPath(projectDirectory, config.tokens), "tokens.stylex.ts");
20
- const relativeTokenPath = relative(dirname(componentPath), tokenPath)
21
- .replace(/\.ts$/, "")
22
- .split(sep)
23
- .join("/");
24
- const tokenImport = relativeTokenPath.startsWith(".")
25
- ? relativeTokenPath
26
- : `./${relativeTokenPath}`;
19
+ const tokenImport = `${config.aliases.styles}/semantic.stylex`;
27
20
  const files = [componentPath];
28
21
  for (const file of files) {
29
22
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nooeh/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "nooeh": "./dist/cli.js"
@@ -19,7 +19,7 @@
19
19
  "access": "public"
20
20
  },
21
21
  "dependencies": {
22
- "@nooeh/registry": "0.1.0"
22
+ "@nooeh/registry": "0.1.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.20.1",