@swiftuijs/twill-export 0.1.0
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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +50 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +208 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saiya and twill contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Twill source export
|
|
2
|
+
|
|
3
|
+
`@swiftuijs/twill-export` exports a checked source project to native TS/TSX. It supplies the optional `twill export` subcommand and a programmatic API; applications do not ship it. Node 20.19+ or 22.12+ is required.
|
|
4
|
+
|
|
5
|
+
Install the compiler and optional export package into your application, then run:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add -D @swiftuijs/twill @swiftuijs/twill-export
|
|
9
|
+
pnpm exec twill export -p tsconfig.json -o ../native-project --dry-run --json
|
|
10
|
+
pnpm exec twill export -p tsconfig.json -o ../native-project
|
|
11
|
+
pnpm exec tsc -p ../native-project/tsconfig.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The compiler provides the `twill` executable. It loads this package only when you run `twill export`; there is no separate export executable.
|
|
15
|
+
|
|
16
|
+
Use a **new directory outside the input project**. Original sources and configuration remain intact. The tool rejects an existing destination, overlapping directories, output filename and module-resolution collisions (including case-only collisions), escaping symlinks and type/syntax errors. Preflight completes before output creation; caught write failures remove the newly created incomplete output.
|
|
17
|
+
|
|
18
|
+
The export contains the checked source graph, local declarations, directly imported local assets and a flattened native checking config. `.twill` becomes `.ts`; `.twillx` becomes `.tsx`. Relative literal imports, re-exports, type imports and dynamic imports are rewritten in both dialect and native files. Extensionless imports retain their spelling. Ordinary strings and comments remain unchanged. Dialect output is formatted with Prettier; native sources retain their layout except for rewritten module literals.
|
|
19
|
+
|
|
20
|
+
The checking config preserves resolved TS options and project-local path settings, enables native TS extension imports and removes Twill TS-server plugins. It uses explicit files and `noEmit`; choose your own normal bundler/emission configuration afterward. Generated files use native TS coordinates, without maps to the old Twill source.
|
|
21
|
+
|
|
22
|
+
## Boundaries
|
|
23
|
+
|
|
24
|
+
- This exports sources, not a complete application installation. Package manifests, dependencies, bundler/plugin configuration, public folders and indirectly referenced assets are not copied. Install normal dependencies in the destination or merge the exported sources into an existing native project.
|
|
25
|
+
- One configured source project is supported. Project references and sources outside its root fail before writes; use a shared source configuration covering the desired graph where appropriate.
|
|
26
|
+
- Computed dynamic import paths, CommonJS dialect imports and dialect-specific `import.meta.glob` patterns require explicit ES module paths before export. The tool does not guess runtime paths.
|
|
27
|
+
- Explicit dialect extensions in aliased imports need relative paths before export. Same-stem native/dialect modules need distinct names so TS/JS extensionless resolution cannot change after renaming.
|
|
28
|
+
- Path aliases inherited without an explicit `baseUrl`, or path-valued compiler settings outside the source root, fail with a diagnostic. Native TypeScript/framework runtime settings remain the application's responsibility.
|
|
29
|
+
- Formatting is for readable native source. Compiler/build source maps remain the appropriate way to debug the original Twill project.
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { exportProject } from '@swiftuijs/twill-export';
|
|
35
|
+
|
|
36
|
+
const result = await exportProject('/app/tsconfig.json', {
|
|
37
|
+
outDir: '/native-app',
|
|
38
|
+
dryRun: true,
|
|
39
|
+
});
|
|
40
|
+
console.log(result.diagnostics, result.files, result.written);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Source errors return diagnostics and `written: false`. Invalid destinations and unsupported export constructs throw. `--json` provides the same structured result; failures return a nonzero CLI status.
|
|
44
|
+
|
|
45
|
+
[Guide](https://swiftuijs.github.io/twill/adoption) · [CLI reference](https://swiftuijs.github.io/twill/cli) · [Issues](https://github.com/swiftuijs/twill/issues) · MIT licensed · Built by [forth.ink](https://forth.ink).
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function main(args?: string[]): Promise<number>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { exportProject } from "./index.js";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
//#region src/cli.ts
|
|
5
|
+
async function main(args = process.argv.slice(2)) {
|
|
6
|
+
const { values, positionals } = parseArgs({
|
|
7
|
+
args,
|
|
8
|
+
options: {
|
|
9
|
+
project: {
|
|
10
|
+
type: "string",
|
|
11
|
+
short: "p"
|
|
12
|
+
},
|
|
13
|
+
out: {
|
|
14
|
+
type: "string",
|
|
15
|
+
short: "o"
|
|
16
|
+
},
|
|
17
|
+
"dry-run": {
|
|
18
|
+
type: "boolean",
|
|
19
|
+
default: false
|
|
20
|
+
},
|
|
21
|
+
json: {
|
|
22
|
+
type: "boolean",
|
|
23
|
+
default: false
|
|
24
|
+
},
|
|
25
|
+
help: {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
short: "h"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (values.help) {
|
|
32
|
+
console.log("twill export [-p tsconfig.json] -o ../native-project [--dry-run] [--json]\nExports checked sources into a new directory outside the input project. Originals remain intact.");
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
if (positionals.length || !values.out) throw new Error("Specify --out <new directory>. Use --help for usage.");
|
|
36
|
+
const result = await exportProject(resolve(values.project ?? "tsconfig.json"), {
|
|
37
|
+
outDir: resolve(values.out),
|
|
38
|
+
dryRun: values["dry-run"]
|
|
39
|
+
});
|
|
40
|
+
if (values.json) console.log(JSON.stringify(result, null, 2));
|
|
41
|
+
else {
|
|
42
|
+
for (const diagnostic of result.diagnostics) console.error(`${diagnostic.filename ?? "tsconfig"}:${diagnostic.line}:${diagnostic.column + 1} TS${diagnostic.code}: ${diagnostic.message}`);
|
|
43
|
+
if (!result.diagnostics.some((item) => item.category === "error")) console.log(`${result.written ? "Exported" : "Planned"} ${result.files.length} files; native config: ${result.tsconfig}`);
|
|
44
|
+
}
|
|
45
|
+
return result.diagnostics.some((item) => item.category === "error") ? 1 : 0;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { main };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { parseArgs } from 'node:util';\nimport { resolve } from 'node:path';\nimport { exportProject } from './index.js';\n\nexport async function main(args = process.argv.slice(2)): Promise<number> {\n const { values, positionals } = parseArgs({\n args,\n options: {\n project: { type: 'string', short: 'p' },\n out: { type: 'string', short: 'o' },\n 'dry-run': { type: 'boolean', default: false },\n json: { type: 'boolean', default: false },\n help: { type: 'boolean', short: 'h' },\n },\n });\n if (values.help) {\n console.log(\n 'twill export [-p tsconfig.json] -o ../native-project [--dry-run] [--json]\\nExports checked sources into a new directory outside the input project. Originals remain intact.',\n );\n return 0;\n }\n if (positionals.length || !values.out)\n throw new Error('Specify --out <new directory>. Use --help for usage.');\n const result = await exportProject(resolve(values.project ?? 'tsconfig.json'), {\n outDir: resolve(values.out),\n dryRun: values['dry-run'],\n });\n if (values.json) console.log(JSON.stringify(result, null, 2));\n else {\n for (const diagnostic of result.diagnostics)\n console.error(\n `${diagnostic.filename ?? 'tsconfig'}:${diagnostic.line}:${diagnostic.column + 1} TS${diagnostic.code}: ${diagnostic.message}`,\n );\n if (!result.diagnostics.some((item) => item.category === 'error'))\n console.log(\n `${result.written ? 'Exported' : 'Planned'} ${result.files.length} files; native config: ${result.tsconfig}`,\n );\n }\n return result.diagnostics.some((item) => item.category === 'error') ? 1 : 0;\n}\n"],"mappings":";;;;AAIA,eAAsB,KAAK,OAAO,QAAQ,KAAK,MAAM,CAAC,GAAoB;CACxE,MAAM,EAAE,QAAQ,gBAAgB,UAAU;EACxC;EACA,SAAS;GACP,SAAS;IAAE,MAAM;IAAU,OAAO;GAAI;GACtC,KAAK;IAAE,MAAM;IAAU,OAAO;GAAI;GAClC,WAAW;IAAE,MAAM;IAAW,SAAS;GAAM;GAC7C,MAAM;IAAE,MAAM;IAAW,SAAS;GAAM;GACxC,MAAM;IAAE,MAAM;IAAW,OAAO;GAAI;EACtC;CACF,CAAC;CACD,IAAI,OAAO,MAAM;EACf,QAAQ,IACN,6KACF;EACA,OAAO;CACT;CACA,IAAI,YAAY,UAAU,CAAC,OAAO,KAChC,MAAM,IAAI,MAAM,sDAAsD;CACxE,MAAM,SAAS,MAAM,cAAc,QAAQ,OAAO,WAAW,eAAe,GAAG;EAC7E,QAAQ,QAAQ,OAAO,GAAG;EAC1B,QAAQ,OAAO;CACjB,CAAC;CACD,IAAI,OAAO,MAAM,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;MACvD;EACH,KAAK,MAAM,cAAc,OAAO,aAC9B,QAAQ,MACN,GAAG,WAAW,YAAY,WAAW,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS,EAAE,KAAK,WAAW,KAAK,IAAI,WAAW,SACvH;EACF,IAAI,CAAC,OAAO,YAAY,MAAM,SAAS,KAAK,aAAa,OAAO,GAC9D,QAAQ,IACN,GAAG,OAAO,UAAU,aAAa,UAAU,GAAG,OAAO,MAAM,OAAO,yBAAyB,OAAO,UACpG;CACJ;CACA,OAAO,OAAO,YAAY,MAAM,SAAS,KAAK,aAAa,OAAO,IAAI,IAAI;AAC5E"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ProjectDiagnostic } from '@swiftuijs/twill/project';
|
|
2
|
+
export interface ExportOptions {
|
|
3
|
+
outDir: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ExportFile {
|
|
7
|
+
source: string;
|
|
8
|
+
output: string;
|
|
9
|
+
kind: 'dialect' | 'native' | 'asset';
|
|
10
|
+
}
|
|
11
|
+
export interface ExportResult {
|
|
12
|
+
sourceRoot: string;
|
|
13
|
+
outDir: string;
|
|
14
|
+
tsconfig: string;
|
|
15
|
+
files: ExportFile[];
|
|
16
|
+
diagnostics: ProjectDiagnostic[];
|
|
17
|
+
written: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Export checked source files, not application dependencies or bundler configuration.
|
|
20
|
+
* Preflight finishes before creating an exclusively owned destination directory. */
|
|
21
|
+
export declare function exportProject(tsconfig: string, options: ExportOptions): Promise<ExportResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import MagicString from "magic-string";
|
|
3
|
+
import { TwillProject, isDependency, sourceFilename } from "@swiftuijs/twill/project";
|
|
4
|
+
import { isTwillFile } from "@swiftuijs/twill";
|
|
5
|
+
import { formatGenerated } from "@swiftuijs/twill-formatter";
|
|
6
|
+
import { lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
var nativeName = (name) => name.replace(/\.twillx$/, ".tsx").replace(/\.twill$/, ".ts");
|
|
10
|
+
var portable = (path) => path.replaceAll("\\", "/");
|
|
11
|
+
var inside = (root, path) => {
|
|
12
|
+
const name = relative(root, path);
|
|
13
|
+
return !isAbsolute(name) && name !== ".." && !name.startsWith(".." + (process.platform === "win32" ? "\\" : "/"));
|
|
14
|
+
};
|
|
15
|
+
function exists(path) {
|
|
16
|
+
try {
|
|
17
|
+
lstatSync(path);
|
|
18
|
+
return true;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error.code === "ENOENT") return false;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function canonical(path) {
|
|
25
|
+
return exists(path) ? realpathSync(path) : join(canonical(dirname(path)), basename(path));
|
|
26
|
+
}
|
|
27
|
+
/** Export checked source files, not application dependencies or bundler configuration.
|
|
28
|
+
* Preflight finishes before creating an exclusively owned destination directory. */
|
|
29
|
+
async function exportProject(tsconfig, options) {
|
|
30
|
+
tsconfig = resolve(tsconfig);
|
|
31
|
+
const root = dirname(tsconfig);
|
|
32
|
+
const out = resolve(options.outDir);
|
|
33
|
+
const realRoot = realpathSync(root);
|
|
34
|
+
const realOut = canonical(out);
|
|
35
|
+
if (inside(root, out) || inside(out, root) || inside(realRoot, realOut) || inside(realOut, realRoot)) throw new Error("Export directory must be outside the source project and cannot contain it.");
|
|
36
|
+
if (exists(out)) throw new Error(`Export directory already exists: ${out}`);
|
|
37
|
+
const result = {
|
|
38
|
+
sourceRoot: root,
|
|
39
|
+
outDir: out,
|
|
40
|
+
tsconfig: join(out, "tsconfig.json"),
|
|
41
|
+
files: [],
|
|
42
|
+
diagnostics: [],
|
|
43
|
+
written: false
|
|
44
|
+
};
|
|
45
|
+
const project = new TwillProject(tsconfig);
|
|
46
|
+
try {
|
|
47
|
+
result.diagnostics = project.diagnostics();
|
|
48
|
+
if (result.diagnostics.some((item) => item.category === "error")) return result;
|
|
49
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(tsconfig, {}, {
|
|
50
|
+
...ts.sys,
|
|
51
|
+
onUnRecoverableConfigFileDiagnostic() {}
|
|
52
|
+
});
|
|
53
|
+
const raw = ts.readConfigFile(tsconfig, ts.sys.readFile).config;
|
|
54
|
+
if (!parsed || !raw) throw new Error(`Could not read export configuration: ${tsconfig}`);
|
|
55
|
+
if (parsed.options.paths && !parsed.options.baseUrl && !raw.compilerOptions?.paths) throw new Error("Inherited path aliases need an explicit baseUrl for source export.");
|
|
56
|
+
const content = /* @__PURE__ */ new Map();
|
|
57
|
+
const targets = /* @__PURE__ */ new Map();
|
|
58
|
+
const names = /* @__PURE__ */ new Set(["tsconfig.json"]);
|
|
59
|
+
const moduleSources = /* @__PURE__ */ new Map();
|
|
60
|
+
const sources = /* @__PURE__ */ new Set([...project.sourceFiles(), ...(project.service.getProgram()?.getSourceFiles() ?? []).filter((file) => !isDependency(file.fileName) && inside(root, sourceFilename(file.fileName))).map((file) => sourceFilename(file.fileName))]);
|
|
61
|
+
const register = (source, kind) => {
|
|
62
|
+
source = resolve(source);
|
|
63
|
+
if (!inside(root, source) || !inside(realRoot, realpathSync(source))) throw new Error(`Source is outside the export project: ${source}`);
|
|
64
|
+
const name = portable(nativeName(relative(root, source)));
|
|
65
|
+
const key = name.toLowerCase();
|
|
66
|
+
const output = join(out, name);
|
|
67
|
+
if (targets.has(source)) return targets.get(source);
|
|
68
|
+
if (names.has(key)) throw new Error(`Native output filename collision: ${name}`);
|
|
69
|
+
if (kind !== "asset") {
|
|
70
|
+
const stem = key.replace(/\.[cm]?[jt]sx?$/, "");
|
|
71
|
+
const previous = moduleSources.get(stem);
|
|
72
|
+
if (previous && (isTwillFile(source) || isTwillFile(previous))) throw new Error(`Native output module resolution collision: ${name}`);
|
|
73
|
+
moduleSources.set(stem, source);
|
|
74
|
+
}
|
|
75
|
+
names.add(key);
|
|
76
|
+
targets.set(source, output);
|
|
77
|
+
result.files.push({
|
|
78
|
+
source,
|
|
79
|
+
output,
|
|
80
|
+
kind
|
|
81
|
+
});
|
|
82
|
+
return output;
|
|
83
|
+
};
|
|
84
|
+
for (const source of [...sources].sort()) register(source, isTwillFile(source) ? "dialect" : /\.[cm]?[jt]sx?$/.test(source) ? "native" : "asset");
|
|
85
|
+
for (let index = 0; index < result.files.length; index++) {
|
|
86
|
+
const entry = result.files[index];
|
|
87
|
+
if (entry.kind === "asset") {
|
|
88
|
+
content.set(entry.output, readFileSync(entry.source));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const source = project.transformed(entry.source)?.code ?? project.text(entry.source);
|
|
92
|
+
const file = ts.createSourceFile(entry.output, source, ts.ScriptTarget.Latest, true);
|
|
93
|
+
const edits = new MagicString(source);
|
|
94
|
+
const rewrite = (literal) => {
|
|
95
|
+
const boundary = literal.text.search(/[?#]/);
|
|
96
|
+
const path = boundary < 0 ? literal.text : literal.text.slice(0, boundary);
|
|
97
|
+
const suffix = boundary < 0 ? "" : literal.text.slice(boundary);
|
|
98
|
+
if (!path?.startsWith(".")) {
|
|
99
|
+
if (isTwillFile(path)) throw new Error("Aliased dialect extension imports need relative paths before source export.");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const target = resolve(dirname(entry.source), path);
|
|
103
|
+
if (exists(target) && statSync(target).isFile() && !targets.has(target)) {
|
|
104
|
+
if (/\.[cm]?[jt]sx?$|\.twillx?$/.test(target)) throw new Error(`Imported source is missing from the checked graph: ${target}`);
|
|
105
|
+
register(target, "asset");
|
|
106
|
+
}
|
|
107
|
+
if (isTwillFile(path)) {
|
|
108
|
+
const output = targets.get(target);
|
|
109
|
+
if (!output) throw new Error(`Dialect import is missing from the export graph: ${literal.text}`);
|
|
110
|
+
let value = portable(relative(dirname(entry.output), output));
|
|
111
|
+
if (!value.startsWith(".")) value = "./" + value;
|
|
112
|
+
edits.overwrite(literal.getStart(file), literal.end, JSON.stringify(value + suffix));
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const visit = (node) => {
|
|
116
|
+
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) rewrite(node.moduleSpecifier);
|
|
117
|
+
else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)) rewrite(node.argument.literal);
|
|
118
|
+
else if (ts.isExternalModuleReference(node) && node.expression && ts.isStringLiteral(node.expression)) rewrite(node.expression);
|
|
119
|
+
else if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
120
|
+
const argument = node.arguments[0];
|
|
121
|
+
if (!argument || !ts.isStringLiteralLike(argument)) throw new Error("Computed dynamic imports need explicit module paths before source export.");
|
|
122
|
+
rewrite(argument);
|
|
123
|
+
} else if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.some((argument) => ts.isStringLiteralLike(argument) && /\.twillx?(?:[?#]|$)/.test(argument.text))) throw new Error("CommonJS dialect imports need conversion to ES module imports before source export.");
|
|
124
|
+
else if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isMetaProperty(node.expression.expression) && node.expression.name.text.startsWith("glob") && node.arguments.some((argument) => ts.isStringLiteralLike(argument) && argument.text.includes(".twill"))) throw new Error("Dialect import globs need explicit module paths before source export.");
|
|
125
|
+
ts.forEachChild(node, visit);
|
|
126
|
+
};
|
|
127
|
+
visit(file);
|
|
128
|
+
content.set(entry.output, entry.kind === "dialect" ? await formatGenerated(edits.toString(), { filepath: entry.output }) : edits.toString());
|
|
129
|
+
}
|
|
130
|
+
const compilerOptions = {
|
|
131
|
+
...parsed.options,
|
|
132
|
+
noEmit: true,
|
|
133
|
+
allowJs: true,
|
|
134
|
+
allowImportingTsExtensions: true
|
|
135
|
+
};
|
|
136
|
+
for (const [key, values] of [
|
|
137
|
+
["target", ts.ScriptTarget],
|
|
138
|
+
["module", ts.ModuleKind],
|
|
139
|
+
["moduleResolution", ts.ModuleResolutionKind],
|
|
140
|
+
["moduleDetection", ts.ModuleDetectionKind],
|
|
141
|
+
["newLine", ts.NewLineKind]
|
|
142
|
+
]) if (typeof compilerOptions[key] === "number") compilerOptions[key] = values[compilerOptions[key]];
|
|
143
|
+
if (parsed.options.jsx !== void 0) compilerOptions.jsx = {
|
|
144
|
+
[ts.JsxEmit.Preserve]: "preserve",
|
|
145
|
+
[ts.JsxEmit.React]: "react",
|
|
146
|
+
[ts.JsxEmit.ReactNative]: "react-native",
|
|
147
|
+
[ts.JsxEmit.ReactJSX]: "react-jsx",
|
|
148
|
+
[ts.JsxEmit.ReactJSXDev]: "react-jsxdev"
|
|
149
|
+
}[parsed.options.jsx];
|
|
150
|
+
if (parsed.options.lib) compilerOptions.lib = parsed.options.lib.map((name) => name.replace(/^lib\.(.*)\.d\.ts$/, "$1"));
|
|
151
|
+
if (parsed.options.newLine !== void 0) compilerOptions.newLine = parsed.options.newLine === ts.NewLineKind.LineFeed ? "lf" : "crlf";
|
|
152
|
+
if (compilerOptions.plugins) compilerOptions.plugins = compilerOptions.plugins.filter((plugin) => !["@swiftuijs/twill", "@swiftuijs/twill-vscode-tsserver"].includes(plugin.name));
|
|
153
|
+
for (const key of [
|
|
154
|
+
"configFile",
|
|
155
|
+
"configFilePath",
|
|
156
|
+
"pathsBasePath",
|
|
157
|
+
"outDir",
|
|
158
|
+
"declarationDir",
|
|
159
|
+
"tsBuildInfoFile",
|
|
160
|
+
"incremental",
|
|
161
|
+
"composite"
|
|
162
|
+
]) delete compilerOptions[key];
|
|
163
|
+
for (const key of [
|
|
164
|
+
"baseUrl",
|
|
165
|
+
"rootDir",
|
|
166
|
+
"rootDirs",
|
|
167
|
+
"typeRoots"
|
|
168
|
+
]) {
|
|
169
|
+
const value = parsed.options[key];
|
|
170
|
+
if (value === void 0) continue;
|
|
171
|
+
const rebase = (path) => {
|
|
172
|
+
if (!inside(root, path)) throw new Error(`Compiler option ${key} points outside the export project: ${path}`);
|
|
173
|
+
return portable(relative(root, path)) || ".";
|
|
174
|
+
};
|
|
175
|
+
compilerOptions[key] = Array.isArray(value) ? value.map(rebase) : rebase(value);
|
|
176
|
+
}
|
|
177
|
+
if (compilerOptions.paths) compilerOptions.paths = Object.fromEntries(Object.entries(compilerOptions.paths).map(([key, values]) => [key, values.map(nativeName)]));
|
|
178
|
+
const config = {
|
|
179
|
+
compilerOptions,
|
|
180
|
+
files: result.files.filter((file) => file.kind !== "asset").map((file) => portable(relative(out, file.output)))
|
|
181
|
+
};
|
|
182
|
+
content.set(result.tsconfig, JSON.stringify(config, null, 2) + "\n");
|
|
183
|
+
if (!options.dryRun) {
|
|
184
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
185
|
+
mkdirSync(out);
|
|
186
|
+
try {
|
|
187
|
+
for (const [filename, text] of content) {
|
|
188
|
+
mkdirSync(dirname(filename), { recursive: true });
|
|
189
|
+
writeFileSync(filename, text, { flag: "wx" });
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
rmSync(out, {
|
|
193
|
+
recursive: true,
|
|
194
|
+
force: true
|
|
195
|
+
});
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
result.written = true;
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
} finally {
|
|
202
|
+
project.dispose();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { exportProject };
|
|
207
|
+
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import ts from 'typescript';\nimport MagicString from 'magic-string';\nimport { TwillProject, isDependency, sourceFilename } from '@swiftuijs/twill/project';\nimport { isTwillFile } from '@swiftuijs/twill';\nimport { formatGenerated } from '@swiftuijs/twill-formatter';\nimport type { ProjectDiagnostic } from '@swiftuijs/twill/project';\nimport {\n readFileSync,\n realpathSync,\n lstatSync,\n statSync,\n mkdirSync,\n writeFileSync,\n rmSync,\n} from 'node:fs';\nimport { resolve, relative, dirname, isAbsolute, basename, join } from 'node:path';\n\nexport interface ExportOptions {\n outDir: string;\n dryRun?: boolean;\n}\nexport interface ExportFile {\n source: string;\n output: string;\n kind: 'dialect' | 'native' | 'asset';\n}\nexport interface ExportResult {\n sourceRoot: string;\n outDir: string;\n tsconfig: string;\n files: ExportFile[];\n diagnostics: ProjectDiagnostic[];\n written: boolean;\n}\nconst nativeName = (name: string) => name.replace(/\\.twillx$/, '.tsx').replace(/\\.twill$/, '.ts');\nconst portable = (path: string) => path.replaceAll('\\\\', '/');\nconst inside = (root: string, path: string) => {\n const name = relative(root, path);\n return (\n !isAbsolute(name) &&\n name !== '..' &&\n !name.startsWith('..' + (process.platform === 'win32' ? '\\\\' : '/'))\n );\n};\nfunction exists(path: string) {\n try {\n lstatSync(path);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;\n throw error;\n }\n}\nfunction canonical(path: string): string {\n return exists(path) ? realpathSync(path) : join(canonical(dirname(path)), basename(path));\n}\n\n/** Export checked source files, not application dependencies or bundler configuration.\n * Preflight finishes before creating an exclusively owned destination directory. */\nexport async function exportProject(\n tsconfig: string,\n options: ExportOptions,\n): Promise<ExportResult> {\n tsconfig = resolve(tsconfig);\n const root = dirname(tsconfig);\n const out = resolve(options.outDir);\n const realRoot = realpathSync(root);\n const realOut = canonical(out);\n if (\n inside(root, out) ||\n inside(out, root) ||\n inside(realRoot, realOut) ||\n inside(realOut, realRoot)\n )\n throw new Error('Export directory must be outside the source project and cannot contain it.');\n if (exists(out)) throw new Error(`Export directory already exists: ${out}`);\n const result: ExportResult = {\n sourceRoot: root,\n outDir: out,\n tsconfig: join(out, 'tsconfig.json'),\n files: [],\n diagnostics: [],\n written: false,\n };\n const project = new TwillProject(tsconfig);\n try {\n result.diagnostics = project.diagnostics();\n if (result.diagnostics.some((item) => item.category === 'error')) return result;\n const parsed = ts.getParsedCommandLineOfConfigFile(\n tsconfig,\n {},\n { ...ts.sys, onUnRecoverableConfigFileDiagnostic() {} },\n );\n const raw = ts.readConfigFile(tsconfig, ts.sys.readFile).config;\n if (!parsed || !raw) throw new Error(`Could not read export configuration: ${tsconfig}`);\n if (parsed.options.paths && !parsed.options.baseUrl && !raw.compilerOptions?.paths)\n throw new Error('Inherited path aliases need an explicit baseUrl for source export.');\n const content = new Map<string, string | Buffer>();\n const targets = new Map<string, string>();\n const names = new Set<string>(['tsconfig.json']);\n const moduleSources = new Map<string, string>();\n const sources = new Set([\n ...project.sourceFiles(),\n ...(project.service.getProgram()?.getSourceFiles() ?? [])\n .filter(\n (file) => !isDependency(file.fileName) && inside(root, sourceFilename(file.fileName)),\n )\n .map((file) => sourceFilename(file.fileName)),\n ]);\n const register = (source: string, kind: ExportFile['kind']) => {\n source = resolve(source);\n if (!inside(root, source) || !inside(realRoot, realpathSync(source)))\n throw new Error(`Source is outside the export project: ${source}`);\n const name = portable(nativeName(relative(root, source)));\n // Reject names that would collide on a case-insensitive filesystem too.\n const key = name.toLowerCase();\n const output = join(out, name);\n if (targets.has(source)) return targets.get(source)!;\n if (names.has(key)) throw new Error(`Native output filename collision: ${name}`);\n if (kind !== 'asset') {\n const stem = key.replace(/\\.[cm]?[jt]sx?$/, '');\n const previous = moduleSources.get(stem);\n if (previous && (isTwillFile(source) || isTwillFile(previous)))\n throw new Error(`Native output module resolution collision: ${name}`);\n moduleSources.set(stem, source);\n }\n names.add(key);\n targets.set(source, output);\n result.files.push({ source, output, kind });\n return output;\n };\n for (const source of [...sources].sort())\n register(\n source,\n isTwillFile(source) ? 'dialect' : /\\.[cm]?[jt]sx?$/.test(source) ? 'native' : 'asset',\n );\n for (let index = 0; index < result.files.length; index++) {\n const entry = result.files[index]!;\n if (entry.kind === 'asset') {\n content.set(entry.output, readFileSync(entry.source));\n continue;\n }\n const source = project.transformed(entry.source)?.code ?? project.text(entry.source)!;\n const file = ts.createSourceFile(entry.output, source, ts.ScriptTarget.Latest, true);\n const edits = new MagicString(source);\n const rewrite = (literal: ts.StringLiteralLike) => {\n const boundary = literal.text.search(/[?#]/);\n const path = boundary < 0 ? literal.text : literal.text.slice(0, boundary);\n const suffix = boundary < 0 ? '' : literal.text.slice(boundary);\n if (!path?.startsWith('.')) {\n if (isTwillFile(path))\n throw new Error(\n 'Aliased dialect extension imports need relative paths before source export.',\n );\n return;\n }\n const target = resolve(dirname(entry.source), path);\n if (exists(target) && statSync(target).isFile() && !targets.has(target)) {\n if (/\\.[cm]?[jt]sx?$|\\.twillx?$/.test(target))\n throw new Error(`Imported source is missing from the checked graph: ${target}`);\n register(target, 'asset');\n }\n if (isTwillFile(path)) {\n const output = targets.get(target);\n if (!output)\n throw new Error(`Dialect import is missing from the export graph: ${literal.text}`);\n let value = portable(relative(dirname(entry.output), output));\n if (!value.startsWith('.')) value = './' + value;\n edits.overwrite(literal.getStart(file), literal.end, JSON.stringify(value + suffix));\n }\n };\n const visit = (node: ts.Node) => {\n if (\n (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&\n node.moduleSpecifier &&\n ts.isStringLiteral(node.moduleSpecifier)\n )\n rewrite(node.moduleSpecifier);\n else if (\n ts.isImportTypeNode(node) &&\n ts.isLiteralTypeNode(node.argument) &&\n ts.isStringLiteral(node.argument.literal)\n )\n rewrite(node.argument.literal);\n else if (\n ts.isExternalModuleReference(node) &&\n node.expression &&\n ts.isStringLiteral(node.expression)\n )\n rewrite(node.expression);\n else if (\n ts.isCallExpression(node) &&\n node.expression.kind === ts.SyntaxKind.ImportKeyword\n ) {\n const argument = node.arguments[0];\n if (!argument || !ts.isStringLiteralLike(argument))\n throw new Error(\n 'Computed dynamic imports need explicit module paths before source export.',\n );\n rewrite(argument);\n } else if (\n ts.isCallExpression(node) &&\n ts.isIdentifier(node.expression) &&\n node.expression.text === 'require' &&\n node.arguments.some(\n (argument) =>\n ts.isStringLiteralLike(argument) && /\\.twillx?(?:[?#]|$)/.test(argument.text),\n )\n )\n throw new Error(\n 'CommonJS dialect imports need conversion to ES module imports before source export.',\n );\n else if (\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n ts.isMetaProperty(node.expression.expression) &&\n node.expression.name.text.startsWith('glob') &&\n node.arguments.some(\n (argument) => ts.isStringLiteralLike(argument) && argument.text.includes('.twill'),\n )\n )\n throw new Error('Dialect import globs need explicit module paths before source export.');\n ts.forEachChild(node, visit);\n };\n visit(file);\n content.set(\n entry.output,\n entry.kind === 'dialect'\n ? await formatGenerated(edits.toString(), { filepath: entry.output })\n : edits.toString(),\n );\n }\n const compilerOptions: Record<string, any> = {\n ...parsed.options,\n noEmit: true,\n allowJs: true,\n allowImportingTsExtensions: true,\n };\n for (const [key, values] of [\n ['target', ts.ScriptTarget],\n ['module', ts.ModuleKind],\n ['moduleResolution', ts.ModuleResolutionKind],\n ['moduleDetection', ts.ModuleDetectionKind],\n ['newLine', ts.NewLineKind],\n ] as const)\n if (typeof compilerOptions[key] === 'number')\n compilerOptions[key] = values[compilerOptions[key]];\n if (parsed.options.jsx !== undefined)\n compilerOptions.jsx = (\n {\n [ts.JsxEmit.Preserve]: 'preserve',\n [ts.JsxEmit.React]: 'react',\n [ts.JsxEmit.ReactNative]: 'react-native',\n [ts.JsxEmit.ReactJSX]: 'react-jsx',\n [ts.JsxEmit.ReactJSXDev]: 'react-jsxdev',\n } as Record<number, string>\n )[parsed.options.jsx];\n if (parsed.options.lib)\n compilerOptions.lib = parsed.options.lib.map((name) =>\n name.replace(/^lib\\.(.*)\\.d\\.ts$/, '$1'),\n );\n if (parsed.options.newLine !== undefined)\n compilerOptions.newLine = parsed.options.newLine === ts.NewLineKind.LineFeed ? 'lf' : 'crlf';\n if (compilerOptions.plugins)\n compilerOptions.plugins = compilerOptions.plugins.filter(\n (plugin: { name: string }) =>\n !['@swiftuijs/twill', '@swiftuijs/twill-vscode-tsserver'].includes(plugin.name),\n );\n // The result is an independent checking config, with flattened inheritance.\n for (const key of [\n 'configFile',\n 'configFilePath',\n 'pathsBasePath',\n 'outDir',\n 'declarationDir',\n 'tsBuildInfoFile',\n 'incremental',\n 'composite',\n ])\n delete compilerOptions[key];\n for (const key of ['baseUrl', 'rootDir', 'rootDirs', 'typeRoots'] as const) {\n const value = parsed.options[key];\n if (value === undefined) continue;\n const rebase = (path: string) => {\n if (!inside(root, path))\n throw new Error(`Compiler option ${key} points outside the export project: ${path}`);\n return portable(relative(root, path)) || '.';\n };\n compilerOptions[key] = Array.isArray(value) ? value.map(rebase) : rebase(value);\n }\n if (compilerOptions.paths)\n compilerOptions.paths = Object.fromEntries(\n Object.entries(compilerOptions.paths as Record<string, string[]>).map(([key, values]) => [\n key,\n values.map(nativeName),\n ]),\n );\n const config = {\n compilerOptions,\n files: result.files\n .filter((file) => file.kind !== 'asset')\n .map((file) => portable(relative(out, file.output))),\n };\n content.set(result.tsconfig, JSON.stringify(config, null, 2) + '\\n');\n if (!options.dryRun) {\n mkdirSync(dirname(out), { recursive: true });\n mkdirSync(out); // Exclusive claim: never overwrite another directory.\n try {\n for (const [filename, text] of content) {\n mkdirSync(dirname(filename), { recursive: true });\n writeFileSync(filename, text, { flag: 'wx' });\n }\n } catch (error) {\n rmSync(out, { recursive: true, force: true });\n throw error;\n }\n result.written = true;\n }\n return result;\n } finally {\n project.dispose();\n }\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@swiftuijs/twill-export",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Export a checked Twill source project to formatted native TypeScript.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/swiftuijs/twill.git",
|
|
10
|
+
"directory": "packages/export"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://swiftuijs.github.io/twill/",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/swiftuijs/twill/issues"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./cli": {
|
|
27
|
+
"types": "./dist/cli.d.ts",
|
|
28
|
+
"import": "./dist/cli.js",
|
|
29
|
+
"default": "./dist/cli.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"prettier": "^3.9.9",
|
|
40
|
+
"typescript": "5.9.3",
|
|
41
|
+
"magic-string": "^1.4.2",
|
|
42
|
+
"@swiftuijs/twill-formatter": "0.1.0",
|
|
43
|
+
"@swiftuijs/twill": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"vite": "^8.3.2",
|
|
47
|
+
"vite-plugin-dts": "^5.1.2",
|
|
48
|
+
"@types/node": "^26.6.4",
|
|
49
|
+
"vitest": "^5.0.3",
|
|
50
|
+
"@vitest/coverage-v8": "^5.0.3"
|
|
51
|
+
},
|
|
52
|
+
"author": {
|
|
53
|
+
"name": "forth.ink",
|
|
54
|
+
"url": "https://forth.ink"
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"twill",
|
|
58
|
+
"typescript",
|
|
59
|
+
"source-export"
|
|
60
|
+
],
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "vite build",
|
|
63
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
64
|
+
"test:unit": "vitest run",
|
|
65
|
+
"test:watch": "vitest",
|
|
66
|
+
"test:coverage": "vitest run --coverage",
|
|
67
|
+
"test:package": "node tests/consumer.mjs"
|
|
68
|
+
}
|
|
69
|
+
}
|