Skip to content

Commit

Permalink
feat: directly write mjs, dts instead of ts
Browse files Browse the repository at this point in the history
  • Loading branch information
malangcat committed Dec 7, 2024
1 parent 8e7d511 commit b1cfbf2
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 23 deletions.
58 changes: 49 additions & 9 deletions packages/rootage/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import {
buildRootage,
getComponentSpecTs,
getComponentSpecDts,
getComponentSpecIndexDts,
getComponentSpecIndexMjs,
getComponentSpecMjs,
getJsonSchema,
getTokenCss,
getTokenTs,
getTokenDts,
getTokenMjs,
validate,
type Model,
} from "@seed-design/rootage-core";
Expand Down Expand Up @@ -63,10 +67,22 @@ async function prepare() {
async function writeTokenTs() {
const { ctx } = await prepare();

const results = getTokenTs(ctx);
const mjsResults = getTokenMjs(ctx);
const dtsResults = getTokenDts(ctx);

for (const result of results) {
const writePath = path.join(process.cwd(), dir, `${result.path}.vars.ts`);
for (const result of mjsResults) {
const writePath = path.join(process.cwd(), dir, `${result.path}.mjs`);

console.log("Writing", result.path, "to", writePath);

if (!fs.existsSync(path.dirname(writePath))) {
fs.mkdirpSync(path.dirname(writePath));
}
fs.writeFileSync(writePath, result.code);
}

for (const result of dtsResults) {
const writePath = path.join(process.cwd(), dir, `${result.path}.d.ts`);

console.log("Writing", result.path, "to", writePath);

Expand All @@ -78,13 +94,37 @@ async function writeComponentSpec() {
const { ctx } = await prepare();

for (const spec of ctx.componentSpecs) {
const code = getComponentSpecTs(spec.data);
const writePath = path.join(process.cwd(), dir, `${spec.id}.vars.ts`);
const mjsCode = getComponentSpecMjs(ctx, spec.id);
const mjsWritePath = path.join(process.cwd(), dir, `${spec.id}.mjs`);

console.log("Writing", spec.name, "to", writePath);
console.log("Writing", spec.name, "to", mjsWritePath);

fs.writeFileSync(writePath, code);
if (!fs.existsSync(path.dirname(mjsWritePath))) {
fs.mkdirpSync(path.dirname(mjsWritePath));
}
fs.writeFileSync(mjsWritePath, mjsCode);

const dtsCode = getComponentSpecDts(ctx, spec.id);
const dtsWritePath = path.join(process.cwd(), dir, `${spec.id}.d.ts`);

console.log("Writing", spec.name, "to", dtsWritePath);

fs.writeFileSync(dtsWritePath, dtsCode);
}

const mjsIndexCode = getComponentSpecIndexMjs(ctx);
const mjsIndexWritePath = path.join(process.cwd(), dir, "index.mjs");

console.log("Writing index to", mjsIndexWritePath);

fs.writeFileSync(mjsIndexWritePath, mjsIndexCode);

const dtsIndexCode = getComponentSpecIndexDts(ctx);
const dtsIndexWritePath = path.join(process.cwd(), dir, "index.d.ts");

console.log("Writing index to", dtsIndexWritePath);

fs.writeFileSync(dtsIndexWritePath, dtsIndexCode);
}

async function writeTokenCss() {
Expand Down
161 changes: 157 additions & 4 deletions packages/rootage/core/src/languages/typescript.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { expect, test } from "vitest";
import type { Model } from "../types";
import { getTokenTs } from "./typescript";
import YAML from "yaml";
import { buildRootage } from "../build";
import type { Model } from "../types";
import { getComponentSpecDts, getComponentSpecMjs, getTokenDts, getTokenMjs } from "./typescript";

test("getTokenTs should generate typescript codes", () => {
test("getTokenMjs should generate esm definitions", () => {
const models: Model[] = [
{
kind: "Tokens",
Expand Down Expand Up @@ -54,7 +55,7 @@ test("getTokenTs should generate typescript codes", () => {
},
];

const result = getTokenTs(buildRootage(models));
const result = getTokenMjs(buildRootage(models));

expect(result).toMatchInlineSnapshot(`
[
Expand All @@ -74,3 +75,155 @@ test("getTokenTs should generate typescript codes", () => {
]
`);
});

test("getTokenDts should generate typescript definitions", () => {
const models: Model[] = [
{
kind: "Tokens",
metadata: {
id: "2",
name: "color",
},
data: {
collection: "color",
tokens: {
"$color.palette.gray-00": {
values: {
light: "#ffffff",
dark: "#000000",
},
},
"$color.palette.gray-100": {
values: {
light: "#f8f9fa",
dark: "#212529",
},
},
"$color.bg.layer-1": {
values: {
light: "$color.palette.gray-00",
dark: "$color.palette.gray-00",
},
},
},
},
},
{
kind: "Tokens",
metadata: {
id: "3",
name: "unit",
},
data: {
collection: "global",
tokens: {
"$unit.x1_5": {
values: {
default: "6px",
},
},
},
},
},
];

const result = getTokenDts(buildRootage(models));

expect(result).toMatchInlineSnapshot(`
[
{
"code": "export declare const gray00 = \\"var(--seed-v3-color-palette-gray-00)\\";
export declare const gray100 = \\"var(--seed-v3-color-palette-gray-100)\\";",
"path": "color/palette",
},
{
"code": "export declare const layer1 = \\"var(--seed-v3-color-bg-layer-1)\\";",
"path": "color/bg",
},
{
"code": "export declare const x1_5 = \\"var(--seed-v3-unit-x1_5)\\";",
"path": "unit",
},
]
`);
});

test("getComponentSpecMjs should generate esm definitions", () => {
const yaml = `
kind: ComponentSpec
metadata:
id: test
name: test
data:
base:
enabled:
root:
color: "#ffffff"
variant=primary:
enabled:
root:
color: "#000000"
`;
const models = [YAML.parse(yaml)];

const result = getComponentSpecMjs(buildRootage(models), "test");

expect(result).toMatchInlineSnapshot(`
"export const vars = {
\\"base\\": {
\\"enabled\\": {
\\"root\\": {
\\"color\\": \\"#ffffff\\"
}
}
},
\\"variantPrimary\\": {
\\"enabled\\": {
\\"root\\": {
\\"color\\": \\"#000000\\"
}
}
}
}"
`);
});

test("getComponentSpecDts should generate typescript definitions", () => {
const yaml = `
kind: ComponentSpec
metadata:
id: test
name: test
data:
base:
enabled:
root:
color: "#ffffff"
variant=primary:
enabled:
root:
color: "#000000"
`;
const models = [YAML.parse(yaml)];

const result = getComponentSpecDts(buildRootage(models), "test");

expect(result).toMatchInlineSnapshot(`
"export declare const vars: {
\\"base\\": {
\\"enabled\\": {
\\"root\\": {
\\"color\\": \\"#ffffff\\"
}
}
},
\\"variantPrimary\\": {
\\"enabled\\": {
\\"root\\": {
\\"color\\": \\"#000000\\"
}
}
}
}"
`);
});
86 changes: 76 additions & 10 deletions packages/rootage/core/src/languages/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { camelCase } from "change-case";
import type { ComponentSpecExpression, RootageCtx, TokenExpression } from "../types";
import type { RootageCtx, TokenExpression } from "../types";
import { stringifyCssValue, stringifyTokenReference } from "./css";

/**
Expand Down Expand Up @@ -31,7 +31,12 @@ function stringifyStateKey(state: string[]) {
return camelCase(state.join("-"));
}

export function getComponentSpecTs(expressions: ComponentSpecExpression) {
function getComponentSpec(ctx: RootageCtx, componentId: string) {
const expressions = ctx.componentSpecs.find((spec) => spec.id === componentId)?.data;
if (!expressions) {
throw new Error(`Component spec not found: ${componentId}`);
}

const result: Record<string, Record<string, Record<string, Record<string, string>>>> = {};

for (const expression of expressions) {
Expand Down Expand Up @@ -62,10 +67,46 @@ export function getComponentSpecTs(expressions: ComponentSpecExpression) {
result[variantKey] = variant;
}

return result;
}

export function getComponentSpecMjs(ctx: RootageCtx, componentId: string) {
const result = getComponentSpec(ctx, componentId);
return `export const vars = ${JSON.stringify(result, null, 2)}`;
}

export function getTokenTs(ctx: RootageCtx) {
export function getComponentSpecDts(ctx: RootageCtx, componentId: string) {
const result = getComponentSpec(ctx, componentId);
return `export declare const vars: ${JSON.stringify(result, null, 2)}`;
}

export function getComponentSpecIndexMjs(ctx: RootageCtx) {
const result = ctx.componentSpecs.map((spec) => {
return `export { vars as ${camelCase(spec.id, { mergeAmbiguousCharacters: true })} } from "./${spec.id}.mjs";`;
});

return result.join("\n");
}

export function getComponentSpecIndexDts(ctx: RootageCtx) {
const result = ctx.componentSpecs.map((spec) => {
return `export { vars as ${camelCase(spec.id, { mergeAmbiguousCharacters: true })} } from "./${spec.id}";`;
});

return result.join("\n");
}

interface TokenDefinition {
key: string;
value: string;
}

interface TokenGroup {
path: string;
code: TokenDefinition[];
}

function getTokenGroups(ctx: RootageCtx): TokenGroup[] {
const { tokens } = ctx;
const tokenExpressions = tokens.map((decl) => decl.token);

Expand All @@ -81,18 +122,43 @@ export function getTokenTs(ctx: RootageCtx) {
}

return Object.entries(groups).map(([group, expressions]) => {
const definitions = expressions
.map((expression) => {
const key = camelCasePreserveUnderscoreBetweenNumbers(expression.key);
const value = stringifyTokenReference(expression);
const definitions = expressions.map((expression) => {
const key = camelCasePreserveUnderscoreBetweenNumbers(expression.key);
const value = stringifyTokenReference(expression);
return { key, value };
});

return `export const ${key} = "${value}";`;
return {
path: group,
code: definitions,
};
});
}

function generateTokenCode(
groups: TokenGroup[],
isDeclaration: boolean,
): { path: string; code: string }[] {
return groups.map(({ path, code }) => {
const definitions = code
.map(({ key, value }) => {
const exportKeyword = isDeclaration ? "export declare const" : "export const";
return `${exportKeyword} ${key} = "${value}";`;
})
.join("\n");

return {
path: group,
path,
code: definitions,
};
});
}

export function getTokenMjs(ctx: RootageCtx): { path: string; code: string }[] {
const groups = getTokenGroups(ctx);
return generateTokenCode(groups, false);
}

export function getTokenDts(ctx: RootageCtx): { path: string; code: string }[] {
const groups = getTokenGroups(ctx);
return generateTokenCode(groups, true);
}
1 change: 1 addition & 0 deletions packages/rootage/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export interface DependencyGraph {
[tokenRef: TokenRef]: DependencyNode;
}

// TODO: use record instead of array
export interface RootageCtx {
componentSpecs: ComponentSpecDeclaration[];
tokens: TokenDeclaration[];
Expand Down

0 comments on commit b1cfbf2

Please sign in to comment.