Skip to content

Commit d912338

Browse files
authored
Merge pull request #16 from vnphanquang/alter-build-strategy
Proposal: Declarative Build Strategy with Mustache Template
2 parents 877ebea + 6091caf commit d912338

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+556
-590
lines changed

.editorconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Editor configuration, see https://editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
insert_final_newline = true
9+
indent_style = tab
10+
indent_size = 2
11+
trim_trailing_whitespace = true
12+
max_line_length = 100
13+
14+
[*.yaml]
15+
indent_style = space
16+

.github/workflows/tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
- main
66
paths:
77
- "scripts/**/*"
8+
- "tests/**/*"
89
- ".github/workflows/tests.yaml"
910
pull_request:
1011
branches:

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
## 1.3.0 (2024-11-25)
1+
# Changelog
22

33
## 1.4.0
44

55
### Minor Changes
66

77
- [#14](https://github.com/evilmartians/harmony/pull/14) [`d3f6327`](https://github.com/evilmartians/harmony/commit/d3f6327d0113be26d1c3cc0468987700784726fe) Thanks [@vnphanquang](https://github.com/vnphanquang)! - set pacakge as ESM-first, prioritize ESM exports as `.js`, update CommonJS exports as `.cjs`, fix [publint](https://publint.dev/@evilmartians/[email protected]) recommendations
88

9+
## 1.3.0 (2024-11-25)
10+
911
- Add support for Tailwind v4
1012

1113
## 1.2.0 (2023-11-25)

deno.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"tasks": {
33
"build": "deno run --allow-read --allow-write scripts/build.ts",
4-
"publint": "deno run --allow-read --allow-env npm:publint",
5-
"test": "deno test --allow-read --allow-write",
4+
"publint": "deno lint dist && deno check dist && deno run --allow-read --allow-env npm:publint",
5+
"test": "deno test --allow-read --allow-write --watch",
66
"update-snapshots": "deno test --allow-read --allow-write -- --update",
77
"changesets": "deno run -A npm:@changesets/cli",
88
"ci:test": "deno test -A --clean --coverage=coverage --junit-path=coverage/junit.xml",
@@ -20,6 +20,7 @@
2020
},
2121
"nodeModulesDir": "auto",
2222
"imports": {
23+
"@lambdalisue/sandbox": "jsr:@lambdalisue/sandbox@^2.0.1",
2324
"@std/assert": "jsr:@std/assert@^1.0.8",
2425
"@std/path": "jsr:@std/path@^1.0.8",
2526
"@std/testing": "jsr:@std/testing@^1.0.5"

deno.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"devDependencies": {
4444
"@changesets/changelog-github": "^0.5.0",
4545
"@changesets/cli": "^2.27.10",
46+
"mustache": "^4.2.0",
4647
"publint": "^0.2.12",
4748
"zod": "^3.23.8"
4849
},

scripts/build.ts

Lines changed: 8 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,11 @@
1-
import * as z from "zod";
21
import * as path from "@std/path";
3-
import { buildTailwindPalette } from "./targets/tailwind.ts";
4-
import { buildBasicPalette } from "./targets/base.ts";
5-
import { ExportTarget, PaletteWithFallback } from "./types.ts";
6-
import { buildCssVars } from "./targets/cssVariables.ts";
7-
import { buildTailwindv4Palette } from "./targets/tailwind-v4.ts";
2+
import { build } from "./builder.ts";
3+
import base from "./targets/base/base.config.ts";
4+
import css from "./targets/css/css.config.ts";
5+
import tailwind from "./targets/tailwind/tailwind.config.ts";
6+
import source from "../source.json" with { type: "json" };
87

9-
//
10-
// Config
11-
//
12-
const SOURCE_FILE = "./source.json";
13-
const DIST_DIR = path.join(Deno.cwd(), "dist");
14-
const EXPORT_TARGETS = [
15-
{
16-
targetDir: "base",
17-
target: buildBasicPalette,
18-
},
19-
{
20-
targetDir: "tailwind",
21-
target: buildTailwindPalette,
22-
},
23-
{
24-
targetDir: "tailwind",
25-
target: buildTailwindv4Palette,
26-
},
27-
{
28-
targetDir: "css",
29-
target: buildCssVars,
30-
},
31-
];
8+
const configs = [base, css, tailwind];
9+
const dir = path.join(Deno.cwd(), "dist");
3210

33-
//
34-
// SOURCE_FILE schema
35-
//
36-
const SourceSchema = z.record(
37-
z.string().regex(/\w+\/\d+/).toLowerCase().describe(
38-
'Color name and shade. Example: "red/500"',
39-
),
40-
z.object({
41-
$oklch: z.string().describe(
42-
'Color in Oklch format. Example: "oklch(98.83% 0.005 20)"',
43-
),
44-
$srgbFallback: z.string().describe(
45-
'Fallback color in sRGB format. Example: "#ffffff"',
46-
),
47-
}),
48-
);
49-
50-
//
51-
// Main
52-
//
53-
await createDistDir(DIST_DIR);
54-
const palette = await loadPalette(SOURCE_FILE);
55-
await Promise.all(
56-
EXPORT_TARGETS.map(({ targetDir, target }) =>
57-
runExportTarget(path.join(DIST_DIR, targetDir), target, palette)
58-
),
59-
);
60-
61-
//
62-
// Helper functions
63-
//
64-
async function loadPalette(fileName: string): Promise<PaletteWithFallback> {
65-
const content = await Deno.readTextFile(fileName);
66-
const sourcePalette = SourceSchema.parse(JSON.parse(content.toString()));
67-
68-
const result: PaletteWithFallback = {};
69-
70-
for (const [key, value] of Object.entries(sourcePalette)) {
71-
const [name, shade] = key.split("/") as [string, string];
72-
if (!result[name]) {
73-
result[name] = {};
74-
}
75-
result[name][shade] = {
76-
oklch: value.$oklch,
77-
rgbFallback: value.$srgbFallback,
78-
};
79-
}
80-
81-
return result;
82-
}
83-
84-
async function runExportTarget(
85-
targetDir: string,
86-
target: ExportTarget,
87-
palette: PaletteWithFallback,
88-
) {
89-
await Deno.mkdir(targetDir, { recursive: true });
90-
await target({ targetDir, palette });
91-
}
92-
93-
async function createDistDir(dir: string) {
94-
try {
95-
await Deno.remove(dir, { recursive: true });
96-
} catch (err) {
97-
if (!(err instanceof Deno.errors.NotFound)) {
98-
throw err;
99-
}
100-
}
101-
await Deno.mkdir(dir);
102-
}
11+
await build(source, dir, configs);

scripts/builder.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { stub } from "@std/testing/mock";
2+
import { assert, assertRejects } from "@std/assert";
3+
import { sandbox } from "@lambdalisue/sandbox";
4+
5+
import { testPaletteSource } from "../tests/utils.ts";
6+
import base from "./targets/base/base.config.ts";
7+
import { build } from "./builder.ts";
8+
9+
Deno.test("should abort and clean up if Deno.remove throws unexpect error", async () => {
10+
const errorMessage = "some Deno.remove error";
11+
const stubbed = stub(Deno, "remove", () => {
12+
throw new Error(errorMessage);
13+
});
14+
15+
const sbox = await sandbox();
16+
try {
17+
await build(testPaletteSource, sbox.path, [base]);
18+
} catch (e) {
19+
assert(e instanceof Error);
20+
assert(e.message === errorMessage);
21+
} finally {
22+
stubbed.restore();
23+
await sbox[Symbol.asyncDispose]();
24+
}
25+
assert(stubbed.calls.length === 1);
26+
await assertRejects(() => Deno.lstat(sbox.path));
27+
});

scripts/builder.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import * as z from "zod";
2+
import { BuildConfig, PaletteWithFallback } from "./types.ts";
3+
import mustache from "mustache";
4+
import * as path from "@std/path";
5+
6+
//
7+
// SOURCE_FILE schema
8+
//
9+
const SourceSchema = z.record(
10+
z.string().regex(/\w+\/\d+/).toLowerCase().describe(
11+
'Color name and shade. Example: "red/500"',
12+
),
13+
z.object({
14+
$oklch: z.string().describe(
15+
'Color in Oklch format. Example: "oklch(98.83% 0.005 20)"',
16+
),
17+
$srgbFallback: z.string().describe(
18+
'Fallback color in sRGB format. Example: "#ffffff"',
19+
),
20+
}),
21+
);
22+
23+
export type PaletteSource = z.infer<typeof SourceSchema>;
24+
25+
export function defineBuildConfig<C extends BuildConfig>(config: C): C {
26+
return config;
27+
}
28+
29+
export async function build(
30+
source: PaletteSource,
31+
dir: string,
32+
configs: BuildConfig[],
33+
): Promise<string[]> {
34+
// STEP 1: parse palette from source
35+
const parsed = SourceSchema.parse(source);
36+
37+
// STEP 2: prepare palette into disgestive array for easier processing during build
38+
const map = new Map<string, PaletteWithFallback[number]>();
39+
for (const [key, value] of Object.entries(parsed)) {
40+
const [colorName, shadeName] = key.split("/");
41+
let color = map.get(colorName);
42+
if (!color) {
43+
color = { colorName, shades: [] };
44+
map.set(colorName, color);
45+
}
46+
color.shades.push({
47+
shadeName,
48+
oklch: value.$oklch,
49+
srgbFallback: value.$srgbFallback,
50+
});
51+
}
52+
const palette: PaletteWithFallback = Array.from(map.values());
53+
54+
// STEP 3: clean output dir to avoid stale files
55+
try {
56+
await Deno.remove(dir, { recursive: true });
57+
} catch (err) {
58+
if (!(err instanceof Deno.errors.NotFound)) {
59+
throw err;
60+
}
61+
}
62+
63+
// STEP 4: build targets
64+
const filepaths = await Promise.all(configs.flatMap((config) => {
65+
const target = typeof config === "function" ? config(palette) : config;
66+
return target.outputs.map(async (output) => {
67+
const template = await Deno.readTextFile(output.templatePath);
68+
const vars = output.templateVars ?? { palette };
69+
const content = mustache.render(template, vars);
70+
const targetDir = path.join(dir, target.dir);
71+
const targetFile = path.join(targetDir, output.file);
72+
await Deno.mkdir(targetDir, { recursive: true });
73+
await Deno.writeTextFile(targetFile, content);
74+
75+
return targetFile;
76+
});
77+
}));
78+
79+
return filepaths;
80+
}

scripts/targets/__snapshots__/base_test.ts.snap

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)