@fragno-dev/create 0.0.2
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/.turbo/turbo-build.log +12 -0
- package/.turbo/turbo-test.log +66 -0
- package/.turbo/turbo-types$colon$check.log +1 -0
- package/CHANGELOG.md +7 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
- package/src/index.ts +84 -0
- package/src/integration.test.ts +97 -0
- package/src/package-json.ts +67 -0
- package/src/utils.test.ts +9 -0
- package/src/utils.ts +69 -0
- package/templates/fragment/AGENTS.md +220 -0
- package/templates/fragment/README.md +16 -0
- package/templates/fragment/package.template.json +65 -0
- package/templates/fragment/src/client/react.ts +7 -0
- package/templates/fragment/src/client/svelte.ts +7 -0
- package/templates/fragment/src/client/vanilla.ts +7 -0
- package/templates/fragment/src/client/vue.ts +7 -0
- package/templates/fragment/src/index.ts +94 -0
- package/templates/fragment/tsconfig.base.json +34 -0
- package/templates/fragment/tsconfig.json +10 -0
- package/templates/optional/builder/esbuild.config.js +34 -0
- package/templates/optional/builder/rollup.config.js +58 -0
- package/templates/optional/builder/rspack.config.js +93 -0
- package/templates/optional/builder/tsdown.config.ts +30 -0
- package/templates/optional/builder/vite.config.ts +25 -0
- package/templates/optional/builder/webpack.config.js +79 -0
- package/tsconfig.json +10 -0
- package/tsdown.config.ts +10 -0
- package/vitest.config.ts +4 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
$ tsdown
|
|
2
|
+
[34mℹ[39m tsdown [2mv0.15.6[22m powered by rolldown [2mv1.0.0-beta.41[22m
|
|
3
|
+
[34mℹ[39m Using tsdown config: [4m/home/runner/work/fragno/fragno/packages/create/tsdown.config.ts[24m
|
|
4
|
+
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
5
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
6
|
+
[34mℹ[39m Build start
|
|
7
|
+
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m4.48 kB[22m [2m│ gzip: 1.51 kB[22m
|
|
8
|
+
[34mℹ[39m [2mdist/[22mindex.js.map [2m8.87 kB[22m [2m│ gzip: 2.80 kB[22m
|
|
9
|
+
[34mℹ[39m [2mdist/[22mindex.d.ts.map [2m0.35 kB[22m [2m│ gzip: 0.22 kB[22m
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m0.40 kB[22m [2m│ gzip: 0.25 kB[22m
|
|
11
|
+
[34mℹ[39m 4 files, total: 14.10 kB
|
|
12
|
+
[32m✔[39m Build complete in [32m13800ms[39m
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
$ vitest run
|
|
2
|
+
|
|
3
|
+
[1m[46m RUN [49m[22m [36mv3.2.4 [39m[90m/home/runner/work/fragno/fragno/packages/create[39m
|
|
4
|
+
[2mCoverage enabled with [22m[33mistanbul[39m
|
|
5
|
+
|
|
6
|
+
[32m✓[39m src/utils.test.ts [2m([22m[2m1 test[22m[2m)[22m[32m 24[2mms[22m[39m
|
|
7
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with rspack
|
|
8
|
+
[22m[39mtemp /tmp/fragment-test-tsdown-1759910775013
|
|
9
|
+
|
|
10
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with tsdown[2m > [22m[2mpackage.json correctly templated
|
|
11
|
+
[22m[39mtemp /tmp/fragment-test-esbuild-1759910775014
|
|
12
|
+
|
|
13
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with esbuild[2m > [22m[2mpackage.json correctly templated
|
|
14
|
+
[22m[39mtemp /tmp/fragment-test-vite-1759910775018
|
|
15
|
+
|
|
16
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with vite[2m > [22m[2mpackage.json correctly templated
|
|
17
|
+
[22m[39mtemp /tmp/fragment-test-rollup-1759910775018
|
|
18
|
+
|
|
19
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with rollup[2m > [22m[2mpackage.json correctly templated
|
|
20
|
+
[22m[39mtemp /tmp/fragment-test-webpack-1759910775018
|
|
21
|
+
|
|
22
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with webpack[2m > [22m[2mpackage.json correctly templated
|
|
23
|
+
[22m[39mtemp /tmp/fragment-test-rspack-1759910775018
|
|
24
|
+
|
|
25
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with vite[2m > [22m[2mcompiles
|
|
26
|
+
[22m[39m
|
|
27
|
+
|
|
28
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with rspack[2m > [22m[2mcompiles
|
|
29
|
+
[22m[39m
|
|
30
|
+
|
|
31
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with tsdown[2m > [22m[2mbuilds
|
|
32
|
+
[22m[39m
|
|
33
|
+
|
|
34
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with esbuild[2m > [22m[2mbuilds
|
|
35
|
+
[22m[39m
|
|
36
|
+
|
|
37
|
+
[90mstdout[2m | src/integration.test.ts
|
|
38
|
+
[22m[39m
|
|
39
|
+
|
|
40
|
+
[90mstdout[2m | src/integration.test.ts[2m > [22m[2mfragment with vite[2m > [22m[2mbuilds
|
|
41
|
+
[22m[39m
|
|
42
|
+
|
|
43
|
+
[32m✓[39m src/integration.test.ts [2m([22m[2m24 tests[22m[2m | [22m[33m1 skipped[39m[2m)[22m[33m 48944[2mms[22m[39m
|
|
44
|
+
[33m[2m✓[22m[39m fragment with tsdown[2m > [22minstalls [33m 5270[2mms[22m[39m
|
|
45
|
+
[33m[2m✓[22m[39m fragment with tsdown[2m > [22mcompiles [33m 12751[2mms[22m[39m
|
|
46
|
+
[33m[2m✓[22m[39m fragment with tsdown[2m > [22mbuilds [33m 22526[2mms[22m[39m
|
|
47
|
+
[33m[2m✓[22m[39m fragment with esbuild[2m > [22minstalls [33m 4957[2mms[22m[39m
|
|
48
|
+
[33m[2m✓[22m[39m fragment with esbuild[2m > [22mcompiles [33m 13190[2mms[22m[39m
|
|
49
|
+
[33m[2m✓[22m[39m fragment with esbuild[2m > [22mbuilds [33m 5102[2mms[22m[39m
|
|
50
|
+
[33m[2m✓[22m[39m fragment with vite[2m > [22minstalls [33m 5743[2mms[22m[39m
|
|
51
|
+
[33m[2m✓[22m[39m fragment with vite[2m > [22mcompiles [33m 14099[2mms[22m[39m
|
|
52
|
+
[33m[2m✓[22m[39m fragment with vite[2m > [22mbuilds [33m 11778[2mms[22m[39m
|
|
53
|
+
[33m[2m✓[22m[39m fragment with rollup[2m > [22minstalls [33m 5212[2mms[22m[39m
|
|
54
|
+
[33m[2m✓[22m[39m fragment with rollup[2m > [22mcompiles [33m 13215[2mms[22m[39m
|
|
55
|
+
[33m[2m✓[22m[39m fragment with webpack[2m > [22minstalls [33m 5278[2mms[22m[39m
|
|
56
|
+
[33m[2m✓[22m[39m fragment with webpack[2m > [22mcompiles [33m 12925[2mms[22m[39m
|
|
57
|
+
[33m[2m✓[22m[39m fragment with webpack[2m > [22mbuilds [33m 29912[2mms[22m[39m
|
|
58
|
+
[33m[2m✓[22m[39m fragment with rspack[2m > [22minstalls [33m 5536[2mms[22m[39m
|
|
59
|
+
[33m[2m✓[22m[39m fragment with rspack[2m > [22mcompiles [33m 12422[2mms[22m[39m
|
|
60
|
+
[33m[2m✓[22m[39m fragment with rspack[2m > [22mbuilds [33m 8061[2mms[22m[39m
|
|
61
|
+
|
|
62
|
+
[2m Test Files [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
|
63
|
+
[2m Tests [22m [1m[32m24 passed[39m[22m[2m | [22m[33m1 skipped[39m[90m (25)[39m
|
|
64
|
+
[2m Start at [22m 08:06:10
|
|
65
|
+
[2m Duration [22m 52.54s[2m (transform 2.20s, setup 0ms, collect 3.60s, tests 48.97s, environment 1ms, prepare 909ms)[22m
|
|
66
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ tsc --noEmit
|
package/CHANGELOG.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type TemplateTypes = "fragment";
|
|
3
|
+
type BuildTools = "esbuild" | "tsdown" | "vite" | "rollup" | "webpack" | "rspack" | "none";
|
|
4
|
+
interface CreateOptions {
|
|
5
|
+
path: string;
|
|
6
|
+
buildTool: BuildTools;
|
|
7
|
+
name: string;
|
|
8
|
+
template: TemplateTypes;
|
|
9
|
+
}
|
|
10
|
+
declare function create(options: CreateOptions): void;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { BuildTools, create };
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";KAMK,aAAA;AAAA,KAEO,UAAA,GAFM,SAAA,GAAA,QAAA,GAAA,MAAA,GAAA,QAAA,GAAA,SAAA,GAAA,QAAA,GAAA,MAAA;AAElB,UAEU,aAAA,CAFY;EAEZ,IAAA,EAAA,MAAA;EAAa,SAAA,EAEV,UAFU;MAEV,EAAA,MAAA;UAED,EAAA,aAAA;;AAGI,iBAAA,MAAA,CAAgB,OAAa,EAAb,aAAa,CAAA,EAAA,IAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
//#region src/utils.ts
|
|
6
|
+
function mkdirp(dir) {
|
|
7
|
+
try {
|
|
8
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9
|
+
} catch (e) {
|
|
10
|
+
if (e instanceof Error && "code" in e && e.code === "EEXIST") return;
|
|
11
|
+
throw e;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function isPlainObject(value) {
|
|
15
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Deep merge plain objects. Arrays and primitives are overwritten, not merged.
|
|
19
|
+
* @param target The target object
|
|
20
|
+
* @param source The source object
|
|
21
|
+
* @returns The merged object
|
|
22
|
+
*/
|
|
23
|
+
function merge(target, source) {
|
|
24
|
+
const result = { ...target };
|
|
25
|
+
for (const key in source) {
|
|
26
|
+
const sourceValue = source[key];
|
|
27
|
+
const targetValue = result[key];
|
|
28
|
+
if (isPlainObject(sourceValue) && isPlainObject(targetValue)) result[key] = merge(targetValue, sourceValue);
|
|
29
|
+
else result[key] = sourceValue;
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function identity(x) {
|
|
34
|
+
return x;
|
|
35
|
+
}
|
|
36
|
+
function copy(from, to, rename = identity) {
|
|
37
|
+
if (!fs.existsSync(from)) return;
|
|
38
|
+
if (fs.statSync(from).isDirectory()) fs.readdirSync(from).forEach((file) => {
|
|
39
|
+
copy(path.join(from, file), path.join(to, rename(file)));
|
|
40
|
+
});
|
|
41
|
+
else {
|
|
42
|
+
mkdirp(path.dirname(to));
|
|
43
|
+
fs.copyFileSync(from, to);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/package-json.ts
|
|
49
|
+
const unpluginFragnoVersion = "^0.0.1";
|
|
50
|
+
const buildToolPkg = {
|
|
51
|
+
none: {},
|
|
52
|
+
tsdown: {
|
|
53
|
+
devDependencies: {
|
|
54
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
55
|
+
tsdown: "^0.11.9"
|
|
56
|
+
},
|
|
57
|
+
scripts: { build: "tsdown" }
|
|
58
|
+
},
|
|
59
|
+
esbuild: {
|
|
60
|
+
devDependencies: {
|
|
61
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
62
|
+
esbuild: "^0.25.10"
|
|
63
|
+
},
|
|
64
|
+
scripts: { build: "./esbuild.config.js" }
|
|
65
|
+
},
|
|
66
|
+
vite: {
|
|
67
|
+
devDependencies: {
|
|
68
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
69
|
+
vite: "^6.0.0"
|
|
70
|
+
},
|
|
71
|
+
scripts: { build: "vite build" }
|
|
72
|
+
},
|
|
73
|
+
rollup: {
|
|
74
|
+
devDependencies: {
|
|
75
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
76
|
+
"@rollup/plugin-node-resolve": "^16.0.2",
|
|
77
|
+
"@rollup/plugin-typescript": "^12.1.4",
|
|
78
|
+
tslib: "^2.8.1",
|
|
79
|
+
rollup: "^4.52.4"
|
|
80
|
+
},
|
|
81
|
+
scripts: { build: "rollup -c" }
|
|
82
|
+
},
|
|
83
|
+
webpack: {
|
|
84
|
+
devDependencies: {
|
|
85
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
86
|
+
webpack: "^5.102.0",
|
|
87
|
+
"webpack-cli": "^6.0.1",
|
|
88
|
+
"ts-loader": "^9.5.1"
|
|
89
|
+
},
|
|
90
|
+
scripts: { build: "webpack" }
|
|
91
|
+
},
|
|
92
|
+
rspack: {
|
|
93
|
+
devDependencies: {
|
|
94
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
95
|
+
"@rspack/core": "^1.5.8",
|
|
96
|
+
"@rspack/cli": "^1.5.8"
|
|
97
|
+
},
|
|
98
|
+
scripts: { build: "rspack build" }
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/index.ts
|
|
104
|
+
function create(options) {
|
|
105
|
+
let pkgOverride = { name: options.name };
|
|
106
|
+
pkgOverride = merge(pkgOverride, buildToolPkg[options.buildTool]);
|
|
107
|
+
if (options.template == "fragment") writeFragmentTemplate(options.path, pkgOverride);
|
|
108
|
+
else throw new Error(`Unsupported template type: ${options.template}`);
|
|
109
|
+
switch (options.buildTool) {
|
|
110
|
+
case "esbuild":
|
|
111
|
+
writeOptionalTemplate(options.path, "builder/esbuild.config.js");
|
|
112
|
+
break;
|
|
113
|
+
case "tsdown":
|
|
114
|
+
writeOptionalTemplate(options.path, "builder/tsdown.config.ts");
|
|
115
|
+
break;
|
|
116
|
+
case "vite":
|
|
117
|
+
writeOptionalTemplate(options.path, "builder/vite.config.ts");
|
|
118
|
+
break;
|
|
119
|
+
case "rollup":
|
|
120
|
+
writeOptionalTemplate(options.path, "builder/rollup.config.js");
|
|
121
|
+
break;
|
|
122
|
+
case "webpack":
|
|
123
|
+
writeOptionalTemplate(options.path, "builder/webpack.config.js");
|
|
124
|
+
break;
|
|
125
|
+
case "rspack":
|
|
126
|
+
writeOptionalTemplate(options.path, "builder/rspack.config.js");
|
|
127
|
+
break;
|
|
128
|
+
case "none": break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function getTemplateDir() {
|
|
132
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
133
|
+
return path.join(__dirname, "..", "templates");
|
|
134
|
+
}
|
|
135
|
+
function writeOptionalTemplate(targetPath, template) {
|
|
136
|
+
const templatePath = path.join(getTemplateDir(), "optional", template);
|
|
137
|
+
const targetFile = path.join(targetPath, path.basename(template));
|
|
138
|
+
copy(templatePath, targetFile);
|
|
139
|
+
}
|
|
140
|
+
function writeFragmentTemplate(targetPath, pkgOverrides) {
|
|
141
|
+
const templateDir = path.join(getTemplateDir(), "fragment");
|
|
142
|
+
copy(templateDir, targetPath, (basename) => {
|
|
143
|
+
if (basename === "package.template.json") return "package.json";
|
|
144
|
+
return basename;
|
|
145
|
+
});
|
|
146
|
+
const packageJsonPath = path.join(targetPath, "package.json");
|
|
147
|
+
const basePkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
148
|
+
const newPkg = merge(basePkg, pkgOverrides);
|
|
149
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(newPkg, null, 2) + "\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
export { create };
|
|
154
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["e: unknown","buildToolPkg: Record<BuildTools, Record<string, unknown>>","pkgOverride: Record<string, unknown>"],"sources":["../src/utils.ts","../src/package-json.ts","../src/index.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport function mkdirp(dir: string): void {\n try {\n fs.mkdirSync(dir, { recursive: true });\n } catch (e: unknown) {\n if (e instanceof Error && \"code\" in e && e.code === \"EEXIST\") return;\n throw e;\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n Object.prototype.toString.call(value) === \"[object Object]\"\n );\n}\n\n/**\n * Deep merge plain objects. Arrays and primitives are overwritten, not merged.\n * @param target The target object\n * @param source The source object\n * @returns The merged object\n */\nexport function merge<T extends Record<string, unknown>>(\n target: T,\n source: Record<string, unknown>,\n): T {\n const result = { ...target } as Record<string, unknown>;\n\n for (const key in source) {\n const sourceValue = source[key];\n const targetValue = result[key];\n\n if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {\n result[key] = merge(targetValue, sourceValue);\n } else {\n result[key] = sourceValue;\n }\n }\n\n return result as T;\n}\n\nfunction identity<T>(x: T): T {\n return x;\n}\n\nexport function copy(\n from: string,\n to: string,\n rename: (basename: string) => string = identity,\n): void {\n if (!fs.existsSync(from)) return;\n\n const stats = fs.statSync(from);\n\n if (stats.isDirectory()) {\n fs.readdirSync(from).forEach((file) => {\n copy(path.join(from, file), path.join(to, rename(file)));\n });\n } else {\n mkdirp(path.dirname(to));\n fs.copyFileSync(from, to);\n }\n}\n","import type { BuildTools } from \"./index\";\n\nconst unpluginFragnoVersion = \"^0.0.1\";\n\nexport const buildToolPkg: Record<BuildTools, Record<string, unknown>> = {\n none: {},\n tsdown: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n tsdown: \"^0.11.9\",\n },\n scripts: {\n build: \"tsdown\",\n },\n },\n esbuild: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n esbuild: \"^0.25.10\",\n },\n scripts: {\n build: \"./esbuild.config.js\",\n },\n },\n vite: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n vite: \"^6.0.0\",\n },\n scripts: {\n build: \"vite build\",\n },\n },\n rollup: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n \"@rollup/plugin-node-resolve\": \"^16.0.2\",\n \"@rollup/plugin-typescript\": \"^12.1.4\",\n tslib: \"^2.8.1\",\n rollup: \"^4.52.4\",\n },\n scripts: {\n build: \"rollup -c\",\n },\n },\n webpack: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n webpack: \"^5.102.0\",\n \"webpack-cli\": \"^6.0.1\",\n \"ts-loader\": \"^9.5.1\",\n },\n scripts: {\n build: \"webpack\",\n },\n },\n rspack: {\n devDependencies: {\n \"@fragno-dev/unplugin-fragno\": unpluginFragnoVersion,\n \"@rspack/core\": \"^1.5.8\",\n \"@rspack/cli\": \"^1.5.8\",\n },\n scripts: {\n build: \"rspack build\",\n },\n },\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { copy, merge } from \"./utils.ts\";\nimport { buildToolPkg } from \"./package-json.ts\";\n\ntype TemplateTypes = \"fragment\";\n// TODO: the others\nexport type BuildTools = \"esbuild\" | \"tsdown\" | \"vite\" | \"rollup\" | \"webpack\" | \"rspack\" | \"none\";\n\ninterface CreateOptions {\n path: string;\n buildTool: BuildTools;\n name: string;\n template: TemplateTypes;\n}\n\nexport function create(options: CreateOptions) {\n let pkgOverride: Record<string, unknown> = { name: options.name };\n\n // Build tool pkg overrides\n pkgOverride = merge(pkgOverride, buildToolPkg[options.buildTool]);\n\n if (options.template == \"fragment\") {\n writeFragmentTemplate(options.path, pkgOverride);\n } else {\n throw new Error(`Unsupported template type: ${options.template}`);\n }\n\n switch (options.buildTool) {\n case \"esbuild\":\n writeOptionalTemplate(options.path, \"builder/esbuild.config.js\");\n break;\n case \"tsdown\":\n writeOptionalTemplate(options.path, \"builder/tsdown.config.ts\");\n break;\n case \"vite\":\n writeOptionalTemplate(options.path, \"builder/vite.config.ts\");\n break;\n case \"rollup\":\n writeOptionalTemplate(options.path, \"builder/rollup.config.js\");\n break;\n case \"webpack\":\n writeOptionalTemplate(options.path, \"builder/webpack.config.js\");\n break;\n case \"rspack\":\n writeOptionalTemplate(options.path, \"builder/rspack.config.js\");\n break;\n case \"none\":\n break;\n }\n}\n\nfunction getTemplateDir(): string {\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.join(__dirname, \"..\", \"templates\");\n}\n\nfunction writeOptionalTemplate(targetPath: string, template: string) {\n const templatePath = path.join(getTemplateDir(), \"optional\", template);\n const targetFile = path.join(targetPath, path.basename(template));\n\n copy(templatePath, targetFile);\n}\n\nfunction writeFragmentTemplate(targetPath: string, pkgOverrides: Record<string, unknown>) {\n const templateDir = path.join(getTemplateDir(), \"fragment\");\n\n // Copy template files\n copy(templateDir, targetPath, (basename) => {\n if (basename === \"package.template.json\") {\n return \"package.json\";\n }\n return basename;\n });\n\n // Update package.json based on chosen options\n const packageJsonPath = path.join(targetPath, \"package.json\");\n const basePkg = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\"));\n const newPkg = merge(basePkg, pkgOverrides);\n\n // Write to disk\n fs.writeFileSync(packageJsonPath, JSON.stringify(newPkg, null, 2) + \"\\n\");\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fragno-dev/create",
|
|
3
|
+
"description": "A library for creating Fragno fragments",
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"bun": "./src/index.ts",
|
|
8
|
+
"development": "./src/index.ts",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"build": "tsdown",
|
|
19
|
+
"build:watch": "tsdown --watch",
|
|
20
|
+
"types:check": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"zod": "^4.0.5"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@fragno-private/typescript-config": "0.0.1",
|
|
28
|
+
"@fragno-private/vitest-config": "0.0.0",
|
|
29
|
+
"@types/node": "^20",
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"@vitest/coverage-istanbul": "^3.2.4",
|
|
32
|
+
"vitest": "^3.2.4"
|
|
33
|
+
},
|
|
34
|
+
"private": false,
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"typescript": "^5"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { copy, merge } from "./utils.ts";
|
|
5
|
+
import { buildToolPkg } from "./package-json.ts";
|
|
6
|
+
|
|
7
|
+
type TemplateTypes = "fragment";
|
|
8
|
+
// TODO: the others
|
|
9
|
+
export type BuildTools = "esbuild" | "tsdown" | "vite" | "rollup" | "webpack" | "rspack" | "none";
|
|
10
|
+
|
|
11
|
+
interface CreateOptions {
|
|
12
|
+
path: string;
|
|
13
|
+
buildTool: BuildTools;
|
|
14
|
+
name: string;
|
|
15
|
+
template: TemplateTypes;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function create(options: CreateOptions) {
|
|
19
|
+
let pkgOverride: Record<string, unknown> = { name: options.name };
|
|
20
|
+
|
|
21
|
+
// Build tool pkg overrides
|
|
22
|
+
pkgOverride = merge(pkgOverride, buildToolPkg[options.buildTool]);
|
|
23
|
+
|
|
24
|
+
if (options.template == "fragment") {
|
|
25
|
+
writeFragmentTemplate(options.path, pkgOverride);
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error(`Unsupported template type: ${options.template}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
switch (options.buildTool) {
|
|
31
|
+
case "esbuild":
|
|
32
|
+
writeOptionalTemplate(options.path, "builder/esbuild.config.js");
|
|
33
|
+
break;
|
|
34
|
+
case "tsdown":
|
|
35
|
+
writeOptionalTemplate(options.path, "builder/tsdown.config.ts");
|
|
36
|
+
break;
|
|
37
|
+
case "vite":
|
|
38
|
+
writeOptionalTemplate(options.path, "builder/vite.config.ts");
|
|
39
|
+
break;
|
|
40
|
+
case "rollup":
|
|
41
|
+
writeOptionalTemplate(options.path, "builder/rollup.config.js");
|
|
42
|
+
break;
|
|
43
|
+
case "webpack":
|
|
44
|
+
writeOptionalTemplate(options.path, "builder/webpack.config.js");
|
|
45
|
+
break;
|
|
46
|
+
case "rspack":
|
|
47
|
+
writeOptionalTemplate(options.path, "builder/rspack.config.js");
|
|
48
|
+
break;
|
|
49
|
+
case "none":
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getTemplateDir(): string {
|
|
55
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
56
|
+
return path.join(__dirname, "..", "templates");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function writeOptionalTemplate(targetPath: string, template: string) {
|
|
60
|
+
const templatePath = path.join(getTemplateDir(), "optional", template);
|
|
61
|
+
const targetFile = path.join(targetPath, path.basename(template));
|
|
62
|
+
|
|
63
|
+
copy(templatePath, targetFile);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeFragmentTemplate(targetPath: string, pkgOverrides: Record<string, unknown>) {
|
|
67
|
+
const templateDir = path.join(getTemplateDir(), "fragment");
|
|
68
|
+
|
|
69
|
+
// Copy template files
|
|
70
|
+
copy(templateDir, targetPath, (basename) => {
|
|
71
|
+
if (basename === "package.template.json") {
|
|
72
|
+
return "package.json";
|
|
73
|
+
}
|
|
74
|
+
return basename;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Update package.json based on chosen options
|
|
78
|
+
const packageJsonPath = path.join(targetPath, "package.json");
|
|
79
|
+
const basePkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
80
|
+
const newPkg = merge(basePkg, pkgOverrides);
|
|
81
|
+
|
|
82
|
+
// Write to disk
|
|
83
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(newPkg, null, 2) + "\n");
|
|
84
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { test, expect, afterAll, beforeAll, describe } from "vitest";
|
|
2
|
+
import { exec } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import { create } from ".";
|
|
8
|
+
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
|
|
11
|
+
async function createTempDir(name: string): Promise<string> {
|
|
12
|
+
const dir = path.join(tmpdir(), `${name}-${Date.now()}`);
|
|
13
|
+
try {
|
|
14
|
+
await fs.rm(dir, { recursive: true });
|
|
15
|
+
} catch {
|
|
16
|
+
// Ignore if directory doesn't exist
|
|
17
|
+
}
|
|
18
|
+
await fs.mkdir(dir, { recursive: true });
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe.concurrent.each(["tsdown", "esbuild", "vite", "rollup", "webpack", "rspack"] as const)(
|
|
23
|
+
"fragment with %s",
|
|
24
|
+
(buildTool) => {
|
|
25
|
+
let tempDir: string;
|
|
26
|
+
const testConfig = {
|
|
27
|
+
name: "@myorg/test",
|
|
28
|
+
template: "fragment" as const,
|
|
29
|
+
buildTool,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
tempDir = await createTempDir(`fragment-test-${buildTool}`);
|
|
34
|
+
console.log("temp", tempDir);
|
|
35
|
+
create({ ...testConfig, path: tempDir });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterAll(async () => {
|
|
39
|
+
await fs.rm(tempDir, { recursive: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe.sequential("", () => {
|
|
43
|
+
test("package.json correctly templated", async () => {
|
|
44
|
+
const pkg = path.join(tempDir, "package.json");
|
|
45
|
+
const pkgContent = await fs.readFile(pkg, "utf8");
|
|
46
|
+
expect(pkgContent).toContain(testConfig.name);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("installs", { timeout: 10000 }, async () => {
|
|
50
|
+
const { stdout } = await execAsync("bun install", {
|
|
51
|
+
cwd: tempDir,
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
});
|
|
54
|
+
expect(stdout).toBeDefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("compiles", { timeout: 30000 }, async () => {
|
|
58
|
+
const { stdout } = await execAsync("bun run types:check", {
|
|
59
|
+
cwd: tempDir,
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
});
|
|
62
|
+
console.log(stdout);
|
|
63
|
+
expect(stdout).toBeDefined();
|
|
64
|
+
});
|
|
65
|
+
/*
|
|
66
|
+
FIXME: Skipping this test for rollup:
|
|
67
|
+
When running rollup directly through bun run build or npm run build the build succeeds,
|
|
68
|
+
but somehow when running through vitest the module resolution mechanism changes causing
|
|
69
|
+
the build to fail.
|
|
70
|
+
*/
|
|
71
|
+
test.skipIf(buildTool == "rollup")("builds", { timeout: 40000 }, async () => {
|
|
72
|
+
const result = await execAsync("bun run build", {
|
|
73
|
+
cwd: tempDir,
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(result).toBeDefined();
|
|
78
|
+
await expect(fs.access(path.join(tempDir, "dist"))).resolves.toBeUndefined();
|
|
79
|
+
await expect(fs.access(path.join(tempDir, "dist", "browser"))).resolves.toBeUndefined();
|
|
80
|
+
|
|
81
|
+
const reactBundle = path.join(tempDir, "dist", "browser", "client", "react.js");
|
|
82
|
+
const reactBundleContent = await fs.readFile(reactBundle, "utf8");
|
|
83
|
+
// We expect the core package to be included in the fragment build,
|
|
84
|
+
// each fragment has its own version of core so end-users don't have
|
|
85
|
+
// to add it to their dependencies.
|
|
86
|
+
expect(reactBundleContent).not.toMatch(/import\s+.*?\s+from\s+['"]@fragno-dev\/core/);
|
|
87
|
+
// However, the peerDependencies of @fragno-dev/core must not be included
|
|
88
|
+
expect(reactBundleContent).toMatch(/from\s*['"]react['"]/);
|
|
89
|
+
|
|
90
|
+
// Vite builds only the browser bundle
|
|
91
|
+
if (buildTool !== "vite") {
|
|
92
|
+
await expect(fs.access(path.join(tempDir, "dist", "node"))).resolves.toBeUndefined();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { BuildTools } from "./index";
|
|
2
|
+
|
|
3
|
+
const unpluginFragnoVersion = "^0.0.1";
|
|
4
|
+
|
|
5
|
+
export const buildToolPkg: Record<BuildTools, Record<string, unknown>> = {
|
|
6
|
+
none: {},
|
|
7
|
+
tsdown: {
|
|
8
|
+
devDependencies: {
|
|
9
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
10
|
+
tsdown: "^0.11.9",
|
|
11
|
+
},
|
|
12
|
+
scripts: {
|
|
13
|
+
build: "tsdown",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
esbuild: {
|
|
17
|
+
devDependencies: {
|
|
18
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
19
|
+
esbuild: "^0.25.10",
|
|
20
|
+
},
|
|
21
|
+
scripts: {
|
|
22
|
+
build: "./esbuild.config.js",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
vite: {
|
|
26
|
+
devDependencies: {
|
|
27
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
28
|
+
vite: "^6.0.0",
|
|
29
|
+
},
|
|
30
|
+
scripts: {
|
|
31
|
+
build: "vite build",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
rollup: {
|
|
35
|
+
devDependencies: {
|
|
36
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
37
|
+
"@rollup/plugin-node-resolve": "^16.0.2",
|
|
38
|
+
"@rollup/plugin-typescript": "^12.1.4",
|
|
39
|
+
tslib: "^2.8.1",
|
|
40
|
+
rollup: "^4.52.4",
|
|
41
|
+
},
|
|
42
|
+
scripts: {
|
|
43
|
+
build: "rollup -c",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
webpack: {
|
|
47
|
+
devDependencies: {
|
|
48
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
49
|
+
webpack: "^5.102.0",
|
|
50
|
+
"webpack-cli": "^6.0.1",
|
|
51
|
+
"ts-loader": "^9.5.1",
|
|
52
|
+
},
|
|
53
|
+
scripts: {
|
|
54
|
+
build: "webpack",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
rspack: {
|
|
58
|
+
devDependencies: {
|
|
59
|
+
"@fragno-dev/unplugin-fragno": unpluginFragnoVersion,
|
|
60
|
+
"@rspack/core": "^1.5.8",
|
|
61
|
+
"@rspack/cli": "^1.5.8",
|
|
62
|
+
},
|
|
63
|
+
scripts: {
|
|
64
|
+
build: "rspack build",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { merge } from "./utils";
|
|
3
|
+
|
|
4
|
+
describe("merge", () => {
|
|
5
|
+
it("merges nested objects", () => {
|
|
6
|
+
const result = merge({ a: { c: 1 } }, { a: { b: 2 } });
|
|
7
|
+
expect(result).toEqual({ a: { c: 1, b: 2 } });
|
|
8
|
+
});
|
|
9
|
+
});
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export function mkdirp(dir: string): void {
|
|
5
|
+
try {
|
|
6
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7
|
+
} catch (e: unknown) {
|
|
8
|
+
if (e instanceof Error && "code" in e && e.code === "EEXIST") return;
|
|
9
|
+
throw e;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
14
|
+
return (
|
|
15
|
+
value !== null &&
|
|
16
|
+
typeof value === "object" &&
|
|
17
|
+
!Array.isArray(value) &&
|
|
18
|
+
Object.prototype.toString.call(value) === "[object Object]"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Deep merge plain objects. Arrays and primitives are overwritten, not merged.
|
|
24
|
+
* @param target The target object
|
|
25
|
+
* @param source The source object
|
|
26
|
+
* @returns The merged object
|
|
27
|
+
*/
|
|
28
|
+
export function merge<T extends Record<string, unknown>>(
|
|
29
|
+
target: T,
|
|
30
|
+
source: Record<string, unknown>,
|
|
31
|
+
): T {
|
|
32
|
+
const result = { ...target } as Record<string, unknown>;
|
|
33
|
+
|
|
34
|
+
for (const key in source) {
|
|
35
|
+
const sourceValue = source[key];
|
|
36
|
+
const targetValue = result[key];
|
|
37
|
+
|
|
38
|
+
if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
|
|
39
|
+
result[key] = merge(targetValue, sourceValue);
|
|
40
|
+
} else {
|
|
41
|
+
result[key] = sourceValue;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result as T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function identity<T>(x: T): T {
|
|
49
|
+
return x;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function copy(
|
|
53
|
+
from: string,
|
|
54
|
+
to: string,
|
|
55
|
+
rename: (basename: string) => string = identity,
|
|
56
|
+
): void {
|
|
57
|
+
if (!fs.existsSync(from)) return;
|
|
58
|
+
|
|
59
|
+
const stats = fs.statSync(from);
|
|
60
|
+
|
|
61
|
+
if (stats.isDirectory()) {
|
|
62
|
+
fs.readdirSync(from).forEach((file) => {
|
|
63
|
+
copy(path.join(from, file), path.join(to, rename(file)));
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
mkdirp(path.dirname(to));
|
|
67
|
+
fs.copyFileSync(from, to);
|
|
68
|
+
}
|
|
69
|
+
}
|