@trineui/cli 0.1.0-beta.1 → 0.1.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.
Files changed (39) hide show
  1. package/README.md +41 -0
  2. package/bin/trine.js +24 -0
  3. package/dist/add-button.js +15 -0
  4. package/dist/add-component.js +317 -0
  5. package/dist/index.js +114 -641
  6. package/package.json +23 -30
  7. package/templates/button/button.html +11 -0
  8. package/templates/button/button.skin.ts +128 -0
  9. package/templates/button/button.ts +39 -0
  10. package/templates/styles/tokens.css +102 -0
  11. package/templates/styles/trine-consumer.css +58 -0
  12. package/CHANGELOG.md +0 -30
  13. package/src/commands/add.ts +0 -101
  14. package/src/commands/diff.test.ts +0 -55
  15. package/src/commands/diff.ts +0 -104
  16. package/src/commands/eject.ts +0 -95
  17. package/src/commands/init.ts +0 -92
  18. package/src/commands/sync-interactive.ts +0 -108
  19. package/src/commands/sync.test.ts +0 -35
  20. package/src/commands/sync.ts +0 -113
  21. package/src/index.ts +0 -18
  22. package/src/types/manifest.ts +0 -14
  23. package/src/utils/__tests__/hash.test.ts +0 -35
  24. package/src/utils/__tests__/template.test.ts +0 -47
  25. package/src/utils/eject-merger.ts +0 -149
  26. package/src/utils/hash.ts +0 -43
  27. package/src/utils/manifest.ts +0 -43
  28. package/src/utils/template.ts +0 -26
  29. package/templates/button.blueprint.ts.hbs +0 -41
  30. package/templates/button.skin.ts.hbs +0 -35
  31. package/templates/checkbox.blueprint.ts.hbs +0 -57
  32. package/templates/checkbox.skin.ts.hbs +0 -44
  33. package/templates/dialog.blueprint.ts.hbs +0 -61
  34. package/templates/dialog.skin.ts.hbs +0 -27
  35. package/templates/input.blueprint.ts.hbs +0 -83
  36. package/templates/input.skin.ts.hbs +0 -29
  37. package/templates/select.blueprint.ts.hbs +0 -86
  38. package/templates/select.skin.ts.hbs +0 -53
  39. package/tsconfig.json +0 -10
@@ -1,104 +0,0 @@
1
- import { Command } from 'commander';
2
- import { readFileSync, existsSync } from 'fs';
3
- import { resolve } from 'path';
4
- import { createTwoFilesPatch } from 'diff';
5
- import { computeNormalizedHash } from '../utils/hash.js';
6
- import { readManifest } from '../utils/manifest.js';
7
- import { renderTemplate } from '../utils/template.js';
8
-
9
- export const diffCommand = new Command('diff')
10
- .argument('<component>', 'Component name (e.g. button)')
11
- .option('--no-color', 'Disable colored output')
12
- .description('Show upstream changes since last sync (read-only, no files changed)')
13
- .action(async (component: string, _options: { noColor?: boolean }) => {
14
- const projectRoot = process.cwd();
15
-
16
- const manifest = readManifest(projectRoot);
17
- if (!manifest?.components[component]) {
18
- console.error(`✗ "${component}" not found in manifest`);
19
- console.error(` Run: trine add ${component}`);
20
- process.exit(1);
21
- }
22
-
23
- const entry = manifest.components[component];
24
-
25
- const blueprintPath = resolve(
26
- projectRoot,
27
- entry['output-dir'],
28
- component,
29
- `${component}.blueprint.ts`
30
- );
31
-
32
- if (!existsSync(blueprintPath)) {
33
- console.error(`✗ Blueprint file not found: ${blueprintPath}`);
34
- console.error(` Run: trine add ${component}`);
35
- process.exit(1);
36
- }
37
-
38
- const currentContent = readFileSync(blueprintPath, 'utf-8');
39
- const currentHash = computeNormalizedHash(currentContent);
40
- const manifestHash = entry['blueprint-hash'];
41
- const isLocalModified = currentHash !== manifestHash;
42
-
43
- const upstreamContent = renderTemplate(`${component}.blueprint.ts.hbs`, {
44
- engineVersion: entry['engine-version'],
45
- blueprintSchemaVersion: entry['blueprint-schema-version'],
46
- });
47
- const upstreamHash = computeNormalizedHash(upstreamContent);
48
- const hasUpstream = upstreamHash !== manifestHash;
49
-
50
- const safeToSync = !isLocalModified && hasUpstream;
51
-
52
- console.log('');
53
- console.log(`${component} blueprint:`);
54
- console.log(
55
- ` local status: ${
56
- isLocalModified
57
- ? '⚠ modified (local changes detected)'
58
- : '✓ clean (unmodified)'
59
- }`
60
- );
61
- console.log(
62
- ` upstream status: ${
63
- hasUpstream
64
- ? `updates available (engine: ${entry['engine-version']} → check changelog)`
65
- : '✓ up to date'
66
- }`
67
- );
68
- console.log(
69
- ` safe to sync: ${
70
- safeToSync
71
- ? 'yes — run: trine sync ' + component
72
- : isLocalModified
73
- ? 'no — run: trine sync ' + component + ' --interactive'
74
- : 'nothing to sync'
75
- }`
76
- );
77
-
78
- if (hasUpstream) {
79
- console.log('');
80
- console.log('--- upstream changes ---');
81
-
82
- const patch = createTwoFilesPatch(
83
- `${component}.blueprint.ts (current)`,
84
- `${component}.blueprint.ts (upstream)`,
85
- currentContent,
86
- upstreamContent,
87
- '',
88
- '',
89
- { context: 3 }
90
- );
91
-
92
- const lines = patch.split('\n').slice(0, 50);
93
- console.log(lines.join('\n'));
94
-
95
- if (patch.split('\n').length > 50) {
96
- console.log(' ... (truncated, use trine sync --interactive for full diff)');
97
- }
98
- }
99
-
100
- console.log('');
101
- console.log(`${component} skin:`);
102
- console.log(` (no upstream — user-owned, never synced)`);
103
- console.log('');
104
- });
@@ -1,95 +0,0 @@
1
- import { confirm } from "@inquirer/prompts";
2
- import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
3
- import { resolve } from "path";
4
- import { Command } from "commander";
5
- import { mergeForEject } from "../utils/eject-merger.js";
6
- import { readManifest, writeManifest } from "../utils/manifest.js";
7
-
8
- export const ejectCommand = new Command("eject")
9
- .argument("<component>", "Component name to eject (e.g. button)")
10
- .option("--yes", "Skip confirmation prompt")
11
- .description(
12
- "Merge blueprint + skin into a standalone component. One-way — no re-attach."
13
- )
14
- .action(
15
- async (component: string, options: { yes?: boolean }) => {
16
- const projectRoot = process.cwd();
17
- const manifest = readManifest(projectRoot);
18
-
19
- if (!manifest?.components[component]) {
20
- console.error(`✗ "${component}" not found in manifest`);
21
- process.exit(1);
22
- }
23
-
24
- const entry = manifest.components[component];
25
- const outputDir = resolve(projectRoot, entry["output-dir"], component);
26
- const blueprintPath = resolve(
27
- outputDir,
28
- `${component}.blueprint.ts`
29
- );
30
- const skinPath = resolve(outputDir, `${component}.skin.ts`);
31
- const outputPath = resolve(outputDir, `${component}.component.ts`);
32
-
33
- if (!existsSync(blueprintPath) || !existsSync(skinPath)) {
34
- console.error(
35
- `✗ Blueprint or skin file not found for "${component}"`
36
- );
37
- process.exit(1);
38
- }
39
-
40
- console.log("");
41
- console.log("⚠️ trine eject — THIS IS ONE-WAY");
42
- console.log("");
43
- console.log(` Component: ${component}`);
44
- console.log(` Output: ${component}.component.ts`);
45
- console.log(
46
- ` Removes: ${component}.blueprint.ts + ${component}.skin.ts`
47
- );
48
- console.log(` Manifest: ${component} entry will be deleted`);
49
- console.log("");
50
- console.log(" Warning: This cannot be undone.");
51
- console.log(
52
- " The component will no longer receive upstream updates."
53
- );
54
- console.log("");
55
-
56
- const confirmed =
57
- options.yes ||
58
- (await confirm({
59
- message: `Eject "${component}"? This is permanent.`,
60
- default: false,
61
- }));
62
-
63
- if (!confirmed) {
64
- console.log("Aborted.");
65
- return;
66
- }
67
-
68
- const blueprintContent = readFileSync(blueprintPath, "utf-8");
69
- const skinContent = readFileSync(skinPath, "utf-8");
70
-
71
- const merged = mergeForEject(
72
- component,
73
- blueprintContent,
74
- skinContent
75
- );
76
-
77
- writeFileSync(outputPath, merged, "utf-8");
78
- console.log(` ✓ ${component}.component.ts written`);
79
-
80
- unlinkSync(blueprintPath);
81
- unlinkSync(skinPath);
82
- console.log(` ✓ ${component}.blueprint.ts removed`);
83
- console.log(` ✓ ${component}.skin.ts removed`);
84
-
85
- delete manifest.components[component];
86
- writeManifest(manifest, projectRoot);
87
- console.log(` ✓ manifest.json updated`);
88
-
89
- console.log("");
90
- console.log(`✓ ${component} ejected.`);
91
- console.log(
92
- ` Edit ${component}.component.ts directly going forward.`
93
- );
94
- }
95
- );
@@ -1,92 +0,0 @@
1
- import { Command } from 'commander';
2
- import { readFileSync, existsSync } from 'fs';
3
- import { resolve, join } from 'path';
4
- import { computeNormalizedHash } from '../utils/hash.js';
5
- import { readManifest, writeManifest } from '../utils/manifest.js';
6
- import type { ComponentEntry, TrineManifest } from '../types/manifest.js';
7
-
8
- const SUPPORTED_COMPONENTS = ['button', 'input', 'dialog', 'checkbox', 'select'];
9
-
10
- export const initCommand = new Command('init')
11
- .description('Scan project and reconstruct manifest.json from existing blueprint files')
12
- .action(async () => {
13
- const projectRoot = process.cwd();
14
-
15
- const manifest = readManifest(projectRoot);
16
- if (manifest) {
17
- console.log('✓ .trine/manifest.json already exists');
18
- console.log(' No need to run trine init.');
19
- console.log('');
20
- console.log(' To check sync status, run: trine diff <component>');
21
- return;
22
- }
23
-
24
- console.log('Scanning for blueprint files...');
25
- console.log('');
26
-
27
- const foundComponents: { name: string; path: string; hash: string }[] = [];
28
- const scanDirs = ['src/components/ui', 'components/ui'];
29
-
30
- for (const dir of scanDirs) {
31
- const basePath = resolve(projectRoot, dir);
32
- if (!existsSync(basePath)) continue;
33
-
34
- for (const component of SUPPORTED_COMPONENTS) {
35
- const blueprintPath = join(basePath, component, `${component}.blueprint.ts`);
36
- if (existsSync(blueprintPath)) {
37
- try {
38
- const content = readFileSync(blueprintPath, 'utf-8');
39
- const hash = computeNormalizedHash(content);
40
- foundComponents.push({ name: component, path: blueprintPath, hash });
41
- console.log(` ✓ Found: ${dir}/${component}/${component}.blueprint.ts`);
42
- } catch {
43
- console.log(` ✗ Error reading: ${dir}/${component}/${component}.blueprint.ts`);
44
- }
45
- }
46
- }
47
- }
48
-
49
- if (foundComponents.length === 0) {
50
- console.log('No blueprint files found.');
51
- console.log('');
52
- console.log(' To add a component, run:');
53
- console.log(' trine add <component>');
54
- console.log('');
55
- console.log(' Supported components: button, input, dialog, checkbox, select');
56
- return;
57
- }
58
-
59
- console.log('');
60
- console.log(`Found ${foundComponents.length} blueprint file(s)`);
61
- console.log('');
62
-
63
- const newManifest: TrineManifest = {
64
- 'trine-spec': '1.0',
65
- components: {},
66
- };
67
-
68
- for (const { name, hash } of foundComponents) {
69
- const entry: ComponentEntry = {
70
- 'engine-version': '0.1.0',
71
- 'blueprint-schema-version': '1.0.0',
72
- 'blueprint-hash': hash,
73
- 'blueprint-modified': true,
74
- 'synced-at': new Date().toISOString(),
75
- 'sync-model': 'normalized-text-v0.1',
76
- 'output-dir': 'src/components/ui',
77
- };
78
- newManifest.components[name] = entry;
79
- }
80
-
81
- writeManifest(newManifest, projectRoot);
82
-
83
- console.log('✓ Created .trine/manifest.json');
84
- console.log('');
85
- console.log('⚠ Warning: blueprint-modified is set to true for all components.');
86
- console.log(' This is conservative — run trine diff <component> to verify sync state.');
87
- console.log('');
88
- console.log(' Next steps:');
89
- console.log(' trine diff button — check button sync status');
90
- console.log(' trine diff input — check input sync status');
91
- console.log(' ...');
92
- });
@@ -1,108 +0,0 @@
1
- import { createTwoFilesPatch, parsePatch } from "diff";
2
- import { select } from "@inquirer/prompts";
3
- import { writeFileSync } from "fs";
4
- import { computeNormalizedHash } from "../utils/hash.js";
5
- import type { TrineManifest } from "../types/manifest.js";
6
-
7
- interface Hunk {
8
- oldStart: number;
9
- oldLines: number;
10
- newStart: number;
11
- newLines: number;
12
- lines: string[];
13
- }
14
-
15
- export async function runInteractiveSync(
16
- component: string,
17
- currentContent: string,
18
- upstreamContent: string,
19
- blueprintPath: string,
20
- manifest: TrineManifest
21
- ): Promise<"merged" | "aborted"> {
22
- const patch = createTwoFilesPatch(
23
- `${component}.blueprint.ts (yours)`,
24
- `${component}.blueprint.ts (upstream)`,
25
- currentContent,
26
- upstreamContent,
27
- "",
28
- "",
29
- { context: 3 }
30
- );
31
-
32
- const parsed = parsePatch(patch);
33
- const hunks: Hunk[] =
34
- parsed.length > 0 && "hunks" in parsed[0]
35
- ? (parsed[0] as unknown as { hunks: Hunk[] }).hunks
36
- : [];
37
-
38
- if (hunks.length === 0) {
39
- console.log("✓ No differences found.");
40
- return "merged";
41
- }
42
-
43
- console.log(
44
- `\nBlueprint has local changes. Upstream has ${hunks.length} changed hunk(s).\n`
45
- );
46
-
47
- const lines = currentContent.split("\n");
48
-
49
- for (let i = 0; i < hunks.length; i++) {
50
- const hunk = hunks[i];
51
- console.log(`--- Hunk ${i + 1}/${hunks.length} ---`);
52
- hunk.lines.forEach((line: string) => {
53
- if (line.startsWith("+"))
54
- console.log(` upstream: ${line.slice(1)}`);
55
- else if (line.startsWith("-"))
56
- console.log(` yours: ${line.slice(1)}`);
57
- });
58
- console.log("");
59
-
60
- const choice = await select({
61
- message: "What do you want to do with this hunk?",
62
- choices: [
63
- {
64
- name: "(k) Keep mine",
65
- value: "keep",
66
- description: "Keep your local version of this hunk",
67
- },
68
- {
69
- name: "(u) Take upstream",
70
- value: "upstream",
71
- description: "Replace with upstream version",
72
- },
73
- {
74
- name: "(s) Skip (keep mine)",
75
- value: "skip",
76
- description: "Skip this hunk, keep your version",
77
- },
78
- ],
79
- });
80
-
81
- if (choice === "upstream") {
82
- applyHunk(lines, hunk, upstreamContent.split("\n"));
83
- }
84
- }
85
-
86
- const mergedContent = lines.join("\n");
87
- writeFileSync(blueprintPath, mergedContent, "utf-8");
88
-
89
- manifest.components[component]["blueprint-hash"] =
90
- computeNormalizedHash(mergedContent);
91
- manifest.components[component]["blueprint-modified"] = false;
92
- manifest.components[component]["synced-at"] = new Date().toISOString();
93
-
94
- console.log("\n✓ Merged. Writing blueprint...");
95
- return "merged";
96
- }
97
-
98
- function applyHunk(
99
- lines: string[],
100
- hunk: Hunk,
101
- upstreamLines: string[]
102
- ): void {
103
- const start = hunk.oldStart - 1;
104
- const end = start + hunk.oldLines;
105
-
106
- const upstreamSection = upstreamLines.slice(start, end);
107
- lines.splice(start, hunk.oldLines, ...upstreamSection);
108
- }
@@ -1,35 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { computeNormalizedHash } from '../utils/hash.js';
3
-
4
- describe('sync logic', () => {
5
- it('same normalized hash → "untouched" path selected', () => {
6
- const content = `@Component({ selector: 'ui-button' })`;
7
- const hash1 = computeNormalizedHash(content);
8
- const hash2 = computeNormalizedHash(content);
9
- expect(hash1).toBe(hash2);
10
- });
11
-
12
- it('different hash → "modified" path selected (dirty detection correct)', () => {
13
- const v1 = `@Component({ selector: 'ui-button' })`;
14
- const v2 = `@Component({ selector: 'ui-btn' })`;
15
- expect(computeNormalizedHash(v1)).not.toBe(computeNormalizedHash(v2));
16
- });
17
-
18
- it('skin content identical before and after sync (skin safety invariant)', () => {
19
- const skinBefore = `@Injectable() export class ButtonSkin { classes = 'btn'; }`;
20
- const skinAfter = skinBefore;
21
- expect(skinBefore).toBe(skinAfter);
22
- });
23
-
24
- it('comment changes do not affect hash (normalized)', () => {
25
- const withComment = `@Component({ selector: 'ui-button' }) // comment`;
26
- const withoutComment = `@Component({ selector: 'ui-button' })`;
27
- expect(computeNormalizedHash(withComment)).toBe(computeNormalizedHash(withoutComment));
28
- });
29
-
30
- it('upstream version change creates different hash', () => {
31
- const v1 = `export class ButtonComponent { protected engine = inject(ButtonEngine); }`;
32
- const v2 = `export class ButtonComponent { static readonly version = '0.2.0'; protected engine = inject(ButtonEngine); }`;
33
- expect(computeNormalizedHash(v1)).not.toBe(computeNormalizedHash(v2));
34
- });
35
- });
@@ -1,113 +0,0 @@
1
- import { Command } from 'commander';
2
- import { writeFileSync, existsSync, readFileSync } from 'fs';
3
- import { resolve } from 'path';
4
- import { computeNormalizedHash } from '../utils/hash.js';
5
- import { readManifest, writeManifest } from '../utils/manifest.js';
6
- import { renderTemplate } from '../utils/template.js';
7
- import { runInteractiveSync } from './sync-interactive.js';
8
-
9
- export const syncCommand = new Command('sync')
10
- .argument('<component>', 'Component name (e.g. button)')
11
- .option('--interactive', 'Interactive 3-way merge for resolving conflicts')
12
- .option('--dry-run', 'Show what would be synced without making changes')
13
- .description('Sync blueprint with latest upstream version')
14
- .action(async (component: string, options: { interactive?: boolean; dryRun?: boolean }) => {
15
- const projectRoot = process.cwd();
16
-
17
- const manifest = readManifest(projectRoot);
18
- if (!manifest?.components[component]) {
19
- console.error(`✗ "${component}" not found in manifest`);
20
- console.error(` Run: trine add ${component}`);
21
- process.exit(1);
22
- }
23
-
24
- const entry = manifest.components[component];
25
-
26
- const blueprintPath = resolve(
27
- projectRoot,
28
- entry['output-dir'],
29
- component,
30
- `${component}.blueprint.ts`
31
- );
32
-
33
- if (!existsSync(blueprintPath)) {
34
- console.error(`✗ Blueprint file not found: ${blueprintPath}`);
35
- console.error(` Run: trine add ${component}`);
36
- process.exit(1);
37
- }
38
-
39
- const currentContent = readFileSync(blueprintPath, 'utf-8');
40
- const currentHash = computeNormalizedHash(currentContent);
41
- const manifestHash = entry['blueprint-hash'];
42
-
43
- const upstreamContent = renderTemplate(`${component}.blueprint.ts.hbs`, {
44
- engineVersion: entry['engine-version'],
45
- blueprintSchemaVersion: entry['blueprint-schema-version'],
46
- });
47
-
48
- const upstreamHash = computeNormalizedHash(upstreamContent);
49
-
50
- if (options.interactive) {
51
- if (currentHash === manifestHash) {
52
- console.log('✓ No local changes to merge.');
53
- return;
54
- }
55
-
56
- const result = await runInteractiveSync(
57
- component,
58
- currentContent,
59
- upstreamContent,
60
- blueprintPath,
61
- manifest
62
- );
63
- if (result === 'merged') {
64
- writeManifest(manifest, projectRoot);
65
- console.log('✓ manifest.json updated.');
66
- }
67
- return;
68
- }
69
-
70
- if (currentHash !== manifestHash) {
71
- console.error('✗ Cannot auto-sync: blueprint has local modifications');
72
- console.error('');
73
- console.error(` Local changes detected in: ${component}.blueprint.ts`);
74
- console.error('');
75
- console.error(' Options:');
76
- console.error(` trine sync ${component} --interactive Resolve conflicts interactively`);
77
- console.error(` trine diff ${component} See what changed upstream`);
78
- console.error(` trine add ${component} --force Overwrite (loses your changes)`);
79
- console.error('');
80
- console.error(' Tip: Run "trine diff" first to understand the changes.');
81
- process.exit(1);
82
- }
83
-
84
- if (upstreamHash === manifestHash) {
85
- console.log(`✓ ${component} blueprint already up to date`);
86
- console.log('');
87
- console.log(' Nothing to sync.');
88
- return;
89
- }
90
-
91
- if (options.dryRun) {
92
- console.log(`[dry-run] Would sync ${component} blueprint`);
93
- console.log(`[dry-run] Blueprint: ${entry['output-dir']}/${component}/${component}.blueprint.ts`);
94
- console.log('[dry-run] Skin: (not touched - user-owned)');
95
- return;
96
- }
97
-
98
- writeFileSync(blueprintPath, upstreamContent, 'utf-8');
99
-
100
- const newHash = computeNormalizedHash(upstreamContent);
101
- entry['blueprint-hash'] = newHash;
102
- entry['synced-at'] = new Date().toISOString();
103
-
104
- writeManifest(manifest, projectRoot);
105
-
106
- console.log(`✓ ${component} blueprint synced successfully`);
107
- console.log('');
108
- console.log(' Updates applied:');
109
- console.log(' blueprint: updated');
110
- console.log(' skin: unchanged (user-owned)');
111
- console.log('');
112
- console.log(` Run: trine diff ${component} — check for more upstream changes`);
113
- });
package/src/index.ts DELETED
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import { addCommand } from './commands/add.js';
4
- import { diffCommand } from './commands/diff.js';
5
- import { syncCommand } from './commands/sync.js';
6
- import { ejectCommand } from './commands/eject.js';
7
- import { initCommand } from './commands/init.js';
8
-
9
- const program = new Command();
10
- program.name('trine').description('Trine CLI').version('0.1.0');
11
-
12
- program.addCommand(addCommand);
13
- program.addCommand(diffCommand);
14
- program.addCommand(syncCommand);
15
- program.addCommand(ejectCommand);
16
- program.addCommand(initCommand);
17
-
18
- program.parse();
@@ -1,14 +0,0 @@
1
- export interface TrineManifest {
2
- 'trine-spec': '1.0';
3
- components: Record<string, ComponentEntry>;
4
- }
5
-
6
- export interface ComponentEntry {
7
- 'engine-version': string;
8
- 'blueprint-schema-version': string;
9
- 'blueprint-hash': string;
10
- 'blueprint-modified': boolean;
11
- 'synced-at': string;
12
- 'sync-model': 'normalized-text-v0.1';
13
- 'output-dir': string;
14
- }
@@ -1,35 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { computeNormalizedHash } from '../hash.js';
3
-
4
- describe('computeNormalizedHash', () => {
5
- it('ค่าเหมือนเดิมหลัง Prettier reformat whitespace', () => {
6
- const original = `@Component({
7
- selector: 'ui-button',
8
- })`;
9
- const prettified = `@Component({ selector: 'ui-button' })`;
10
- expect(computeNormalizedHash(original))
11
- .toBe(computeNormalizedHash(prettified));
12
- });
13
-
14
- it('ค่าเหมือนเดิมหลังเพิ่ม/ลบ comment', () => {
15
- const withComment = `// This is generated
16
- @Component({})`;
17
- const withoutComment = `@Component({})`;
18
- expect(computeNormalizedHash(withComment))
19
- .toBe(computeNormalizedHash(withoutComment));
20
- });
21
-
22
- it('ค่าต่างเมื่อเปลี่ยน structure จริง', () => {
23
- const v1 = `@Component({ selector: 'ui-button' })`;
24
- const v2 = `@Component({ selector: 'ui-btn' })`;
25
- expect(computeNormalizedHash(v1))
26
- .not.toBe(computeNormalizedHash(v2));
27
- });
28
-
29
- it('import reorder ไม่เปลี่ยน hash', () => {
30
- const order1 = `import { A } from 'a';\nimport { B } from 'b';`;
31
- const order2 = `import { B } from 'b';\nimport { A } from 'a';`;
32
- expect(computeNormalizedHash(order1))
33
- .toBe(computeNormalizedHash(order2));
34
- });
35
- });
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { renderTemplate } from '../template.js';
3
-
4
- describe('template rendering', () => {
5
- it('renders blueprint template without unresolved placeholders', () => {
6
- const result = renderTemplate('button.blueprint.ts.hbs', {
7
- engineVersion: '0.1.0',
8
- blueprintSchemaVersion: '1.0.0',
9
- });
10
- expect(result).not.toContain('{{');
11
- expect(result).not.toContain('}}');
12
- expect(result).toContain("selector: 'ui-button'");
13
- expect(result).toContain('ButtonEngine');
14
- expect(result).toContain('BUTTON_STYLING_CONTRACT');
15
- });
16
-
17
- it('renders skin template without ButtonEngine reference', () => {
18
- const result = renderTemplate('button.skin.ts.hbs', {
19
- engineVersion: '0.1.0',
20
- blueprintSchemaVersion: '1.0.0',
21
- });
22
- expect(result).not.toContain('{{');
23
- expect(result).toContain('BUTTON_STYLING_CONTRACT');
24
- expect(result).not.toMatch(/import.*ButtonEngine/);
25
- expect(result).not.toMatch(/inject\(ButtonEngine\)/);
26
- });
27
-
28
- it('injects engine version into blueprint header', () => {
29
- const result = renderTemplate('button.blueprint.ts.hbs', {
30
- engineVersion: '1.2.3',
31
- blueprintSchemaVersion: '2.0.0',
32
- });
33
- expect(result).toContain('1.2.3');
34
- expect(result).toContain('2.0.0');
35
- });
36
-
37
- it('skin template includes all variants including brand', () => {
38
- const result = renderTemplate('button.skin.ts.hbs', {
39
- engineVersion: '0.1.0',
40
- blueprintSchemaVersion: '1.0.0',
41
- });
42
- expect(result).toContain('solid');
43
- expect(result).toContain('outline');
44
- expect(result).toContain('ghost');
45
- expect(result).toContain('brand');
46
- });
47
- });