@codegraft/cli 0.1.0-beta.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 +16 -0
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +43 -0
- package/dist/build.js.map +1 -0
- package/dist/bundle.d.ts +13 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +38 -0
- package/dist/bundle.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/entry-module.d.ts +11 -0
- package/dist/entry-module.d.ts.map +1 -0
- package/dist/entry-module.js +25 -0
- package/dist/entry-module.js.map +1 -0
- package/dist/run.d.ts +45 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +75 -0
- package/dist/run.js.map +1 -0
- package/dist/serialise.d.ts +8 -0
- package/dist/serialise.d.ts.map +1 -0
- package/dist/serialise.js +29 -0
- package/dist/serialise.js.map +1 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Joël Charles
|
|
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,16 @@
|
|
|
1
|
+
# @codegraft/cli
|
|
2
|
+
|
|
3
|
+
The `codegraft` command line for [Codegraft](../../README.md).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Apply a codemod to matched files (defaults to --dry-run). The codemod runs live — its helpers,
|
|
7
|
+
# imports, and deps work as written, with no build step. A grammar target handles its extensions
|
|
8
|
+
# (`tsx` → .tsx/.jsx, …); `.vue` is handled by the cli's built-in splitter (its <script>/<style>
|
|
9
|
+
# zones), so any codemod applies to .vue without declaring it.
|
|
10
|
+
codegraft run <glob...> --codemod <codemod-file> [--context <json>] \
|
|
11
|
+
[--dry-run | --in-place | --out-dir <dir>]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`--codemod` takes a path (`./rule.ts`) or a package specifier (`@codegraft/rules/remove-unused-imports`).
|
|
15
|
+
The file must be importable by the running Node, so a `.ts` codemod needs a loader (Node's
|
|
16
|
+
`--experimental-strip-types`, `tsx`, …) or ship it as `.js`.
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface BuildResult {
|
|
2
|
+
/** Emitted file names under the output dir: a `<stem>.js` per codemod, shared chunks, package.json. */
|
|
3
|
+
files: string[];
|
|
4
|
+
/** Grammar packages the targets require — the optional peers the shipping tool must add. */
|
|
5
|
+
grammarPackages: string[];
|
|
6
|
+
/** Other bare modules the bundle imports (splitters, author npm deps) besides `@codegraft/core`. */
|
|
7
|
+
externalPeers: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* `codegraft build`: bundle one or more codemod files into standalone, `@codegraft/core`-only
|
|
11
|
+
* transformer modules — one `<stem>.js` per codemod plus shared chunks. Each file must be importable
|
|
12
|
+
* by the running Node so its `targets` can be read; rolldown handles the body's TS.
|
|
13
|
+
*/
|
|
14
|
+
export declare function build(codemodFiles: string[], outputDir: string): Promise<BuildResult>;
|
|
15
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,WAAW;IAC1B,uGAAuG;IACvG,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,4FAA4F;IAC5F,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,oGAAoG;IACpG,aAAa,EAAE,MAAM,EAAE,CAAA;CACxB;AAUD;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoB3F"}
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, extname, join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { assert, grammarPackage } from '@codegraft/core/internal';
|
|
5
|
+
import { entryModule } from './entry-module.js';
|
|
6
|
+
import { bundleCodemods } from './bundle.js';
|
|
7
|
+
const isCodemod = (x) => typeof x?.fn === 'function';
|
|
8
|
+
/**
|
|
9
|
+
* `codegraft build`: bundle one or more codemod files into standalone, `@codegraft/core`-only
|
|
10
|
+
* transformer modules — one `<stem>.js` per codemod plus shared chunks. Each file must be importable
|
|
11
|
+
* by the running Node so its `targets` can be read; rolldown handles the body's TS.
|
|
12
|
+
*/
|
|
13
|
+
export async function build(codemodFiles, outputDir) {
|
|
14
|
+
const codemods = await Promise.all(codemodFiles.map(async (file) => {
|
|
15
|
+
const mod = (await import(pathToFileURL(file).href));
|
|
16
|
+
assert(isCodemod(mod.default), `codemod file '${file}' must default-export a defineCodemod result`);
|
|
17
|
+
assert(Array.isArray(mod.targets), `codemod file '${file}' must export a 'targets' array`);
|
|
18
|
+
return { stem: basename(file, extname(file)), file, targets: mod.targets };
|
|
19
|
+
}));
|
|
20
|
+
await mkdir(outputDir, { recursive: true });
|
|
21
|
+
const entries = Object.fromEntries(codemods.map((c) => [c.stem, entryModule(c.file, c.targets)]));
|
|
22
|
+
const { files, externals } = await bundleCodemods(entries, outputDir);
|
|
23
|
+
await writeFile(join(outputDir, 'package.json'), `${JSON.stringify({ type: 'module', sideEffects: false }, null, 2)}\n`);
|
|
24
|
+
return {
|
|
25
|
+
files: [...files, 'package.json'].sort(),
|
|
26
|
+
grammarPackages: grammarPackagesFor(codemods.flatMap((c) => c.targets)),
|
|
27
|
+
externalPeers: externals.filter((id) => id !== '@codegraft/core'),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function grammarPackagesFor(targets) {
|
|
31
|
+
const grammars = new Set();
|
|
32
|
+
for (const target of targets) {
|
|
33
|
+
if (typeof target === 'string')
|
|
34
|
+
grammars.add(target);
|
|
35
|
+
else
|
|
36
|
+
for (const g of target.grammars)
|
|
37
|
+
grammars.add(g);
|
|
38
|
+
}
|
|
39
|
+
// null = a vendored grammar (ships with @codegraft/core), so it needs no peer.
|
|
40
|
+
const packages = [...grammars].map(grammarPackage).filter((p) => p !== null);
|
|
41
|
+
return [...new Set(packages)].sort();
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/bundle.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle one virtual entry per codemod into `outputDir` in a single rolldown run, so common modules
|
|
3
|
+
* (`defineCodemod`, shared helpers) hoist into shared chunks instead of duplicating per codemod.
|
|
4
|
+
* Only the author's relative code and the `defineCodemod` shim are inlined; every bare dependency
|
|
5
|
+
* (`@codegraft/core`, splitters, grammars, npm deps) stays external — nothing heavy is vendored.
|
|
6
|
+
*
|
|
7
|
+
* Returns the emitted file names and the external module ids the bundle imports.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bundleCodemods(entries: Record<string, string>, outputDir: string): Promise<{
|
|
10
|
+
files: string[];
|
|
11
|
+
externals: string[];
|
|
12
|
+
}>;
|
|
13
|
+
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0BnD"}
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path';
|
|
2
|
+
import { rolldown } from 'rolldown';
|
|
3
|
+
const VIRTUAL = '\0codemod-entry:';
|
|
4
|
+
/**
|
|
5
|
+
* Bundle one virtual entry per codemod into `outputDir` in a single rolldown run, so common modules
|
|
6
|
+
* (`defineCodemod`, shared helpers) hoist into shared chunks instead of duplicating per codemod.
|
|
7
|
+
* Only the author's relative code and the `defineCodemod` shim are inlined; every bare dependency
|
|
8
|
+
* (`@codegraft/core`, splitters, grammars, npm deps) stays external — nothing heavy is vendored.
|
|
9
|
+
*
|
|
10
|
+
* Returns the emitted file names and the external module ids the bundle imports.
|
|
11
|
+
*/
|
|
12
|
+
export async function bundleCodemods(entries, outputDir) {
|
|
13
|
+
const input = Object.fromEntries(Object.keys(entries).map((stem) => [stem, VIRTUAL + stem]));
|
|
14
|
+
const bundle = await rolldown({
|
|
15
|
+
input,
|
|
16
|
+
platform: 'node',
|
|
17
|
+
external: (id) => !id.startsWith('.') && !isAbsolute(id) && id !== '@codegraft/codemod',
|
|
18
|
+
plugins: [
|
|
19
|
+
{
|
|
20
|
+
name: 'codegraft:entries',
|
|
21
|
+
resolveId: (id) => (id.startsWith(VIRTUAL) ? id : null),
|
|
22
|
+
load: (id) => (id.startsWith(VIRTUAL) ? entries[id.slice(VIRTUAL.length)] : null),
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
const { output } = await bundle.write({
|
|
27
|
+
dir: outputDir,
|
|
28
|
+
format: 'es',
|
|
29
|
+
entryFileNames: '[name].js',
|
|
30
|
+
chunkFileNames: '_chunks/[name]-[hash].js',
|
|
31
|
+
});
|
|
32
|
+
await bundle.close();
|
|
33
|
+
const chunks = output.filter((part) => part.type === 'chunk');
|
|
34
|
+
const emitted = new Set(chunks.map((chunk) => chunk.fileName));
|
|
35
|
+
const externals = [...new Set(chunks.flatMap((chunk) => chunk.imports))].filter((id) => !emitted.has(id)).sort();
|
|
36
|
+
return { files: output.map((part) => part.fileName).sort(), externals };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAEnC,MAAM,OAAO,GAAG,kBAAkB,CAAA;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAA+B,EAC/B,SAAiB;IAEjB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAC5F,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;QAC5B,KAAK;QACL,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,oBAAoB;QACvF,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,mBAAmB;gBACzB,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;aAClF;SACF;KACF,CAAC,CAAA;IACF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;QACpC,GAAG,EAAE,SAAS;QACd,MAAM,EAAE,IAAI;QACZ,cAAc,EAAE,WAAW;QAC3B,cAAc,EAAE,0BAA0B;KAC3C,CAAC,CAAA;IACF,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IAEpB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;IAC7D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAChH,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,CAAA;AACzE,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Dispatch a `codegraft` invocation. Exported (with an injectable `cwd`) so it is testable
|
|
3
|
+
* without spawning a process; the bin auto-runs it only when invoked directly. */
|
|
4
|
+
export declare function main(argv: string[], cwd?: string): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAUA;mFACmF;AACnF,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrF"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
import { assert } from '@codegraft/core/internal';
|
|
6
|
+
import { run } from './run.js';
|
|
7
|
+
const USAGE = `usage:
|
|
8
|
+
codegraft run <glob...> --codemod <codemod-file> [--context <json>] [--dry-run | --in-place | --out-dir <dir>]`;
|
|
9
|
+
/** Dispatch a `codegraft` invocation. Exported (with an injectable `cwd`) so it is testable
|
|
10
|
+
* without spawning a process; the bin auto-runs it only when invoked directly. */
|
|
11
|
+
export async function main(argv, cwd = process.cwd()) {
|
|
12
|
+
const [command, ...rest] = argv;
|
|
13
|
+
if (command === 'run')
|
|
14
|
+
return cmdRun(rest, cwd);
|
|
15
|
+
throw new Error(`unknown command '${command ?? ''}'\n${USAGE}`);
|
|
16
|
+
}
|
|
17
|
+
async function cmdRun(args, cwd) {
|
|
18
|
+
const { values, positionals } = parseArgs({
|
|
19
|
+
args,
|
|
20
|
+
allowPositionals: true,
|
|
21
|
+
options: {
|
|
22
|
+
codemod: { type: 'string', short: 'c' },
|
|
23
|
+
context: { type: 'string' },
|
|
24
|
+
'dry-run': { type: 'boolean' },
|
|
25
|
+
'in-place': { type: 'boolean' },
|
|
26
|
+
'out-dir': { type: 'string' },
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
assert(positionals.length > 0, `codegraft run: missing <glob>\n${USAGE}`);
|
|
30
|
+
assert(values.codemod, `codegraft run: missing --codemod <codemod-file>\n${USAGE}`);
|
|
31
|
+
const modes = [values['dry-run'], values['in-place'], values['out-dir'] !== undefined].filter(Boolean);
|
|
32
|
+
assert(modes.length <= 1, 'codegraft run: choose at most one of --dry-run, --in-place, --out-dir');
|
|
33
|
+
const mode = values['in-place']
|
|
34
|
+
? { kind: 'in-place' }
|
|
35
|
+
: values['out-dir'] !== undefined
|
|
36
|
+
? { kind: 'out-dir', dir: resolve(cwd, values['out-dir']) }
|
|
37
|
+
: { kind: 'dry-run' }; // safe default: never mutate without an explicit flag
|
|
38
|
+
const result = await run({
|
|
39
|
+
patterns: positionals,
|
|
40
|
+
cwd,
|
|
41
|
+
codemodPath: resolveCodemod(values.codemod, cwd),
|
|
42
|
+
context: values.context ? JSON.parse(values.context) : {},
|
|
43
|
+
mode,
|
|
44
|
+
});
|
|
45
|
+
process.stdout.write(`codegraft run: ${result.transformed.length} transformed, ${result.unchanged.length} unchanged, ${result.skipped.length} skipped\n`);
|
|
46
|
+
}
|
|
47
|
+
/** A `--codemod` value is a path (`./rule.ts`, absolute) resolved against `cwd`, or a package
|
|
48
|
+
* specifier (`@codegraft/rules/remove-unused-imports`) resolved from `cwd`'s node_modules. */
|
|
49
|
+
function resolveCodemod(value, cwd) {
|
|
50
|
+
if (value.startsWith('.') || isAbsolute(value))
|
|
51
|
+
return resolve(cwd, value);
|
|
52
|
+
return fileURLToPath(import.meta.resolve(value, pathToFileURL(join(cwd, '_.js')).href));
|
|
53
|
+
}
|
|
54
|
+
// Auto-run only when this file is the process entry point (not when imported by tests).
|
|
55
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
56
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
57
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,GAAG,EAAgB,MAAM,UAAU,CAAA;AAE5C,MAAM,KAAK,GAAG;iHACmG,CAAA;AAEjH;mFACmF;AACnF,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAc,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACpE,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IAC/B,IAAI,OAAO,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC/C,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,IAAI,EAAE,MAAM,KAAK,EAAE,CAAC,CAAA;AACjE,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,IAAc,EAAE,GAAW;IAC/C,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI;QACJ,gBAAgB,EAAE,IAAI;QACtB,OAAO,EAAE;YACP,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACvC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC9B,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC/B,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC9B;KACF,CAAC,CAAA;IACF,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,kCAAkC,KAAK,EAAE,CAAC,CAAA;IACzE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,oDAAoD,KAAK,EAAE,CAAC,CAAA;IAEnF,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACtG,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,uEAAuE,CAAC,CAAA;IAClG,MAAM,IAAI,GAAY,MAAM,CAAC,UAAU,CAAC;QACtC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE;QACtB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS;YAC/B,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE;YAC3D,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA,CAAC,sDAAsD;IAEhF,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC;QACvB,QAAQ,EAAE,WAAW;QACrB,GAAG;QACH,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC;QAChD,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAA6B,CAAC,CAAC,CAAC,EAAE;QACtF,IAAI;KACL,CAAC,CAAA;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kBAAkB,MAAM,CAAC,WAAW,CAAC,MAAM,iBAAiB,MAAM,CAAC,SAAS,CAAC,MAAM,eAAe,MAAM,CAAC,OAAO,CAAC,MAAM,YAAY,CACpI,CAAA;AACH,CAAC;AAED;+FAC+F;AAC/F,SAAS,cAAc,CAAC,KAAa,EAAE,GAAW;IAChD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC1E,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;AACzF,CAAC;AAED,wFAAwF;AACxF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GrammarId, ZoneSplitter } from '@codegraft/core';
|
|
2
|
+
type Target = GrammarId | ZoneSplitter;
|
|
3
|
+
/**
|
|
4
|
+
* The virtual barrel for one codemod: a `createCodemodTransformer` export per target — named by the
|
|
5
|
+
* grammar/splitter id, so `codegraft run` reads the exports as its target→transformer map. It
|
|
6
|
+
* imports the codemod's source (rolldown bundles it, sharing `defineCodemod`/helpers across
|
|
7
|
+
* codemods) and any splitter package (left external); `namespace` flows through the bundled object.
|
|
8
|
+
*/
|
|
9
|
+
export declare function entryModule(codemodPath: string, targets: Target[]): string;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=entry-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-module.d.ts","sourceRoot":"","sources":["../src/entry-module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9D,KAAK,MAAM,GAAG,SAAS,GAAG,YAAY,CAAA;AAEtC;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAiB1E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { assert } from '@codegraft/core/internal';
|
|
2
|
+
/**
|
|
3
|
+
* The virtual barrel for one codemod: a `createCodemodTransformer` export per target — named by the
|
|
4
|
+
* grammar/splitter id, so `codegraft run` reads the exports as its target→transformer map. It
|
|
5
|
+
* imports the codemod's source (rolldown bundles it, sharing `defineCodemod`/helpers across
|
|
6
|
+
* codemods) and any splitter package (left external); `namespace` flows through the bundled object.
|
|
7
|
+
*/
|
|
8
|
+
export function entryModule(codemodPath, targets) {
|
|
9
|
+
const parts = targets.map((target) => {
|
|
10
|
+
if (typeof target === 'string')
|
|
11
|
+
return { stem: target, expr: JSON.stringify(target), splitter: null };
|
|
12
|
+
assert(target.importName && target.importPath, `splitter '${target.id}' must declare importName and importPath to build`);
|
|
13
|
+
return { stem: target.id, expr: target.importName, splitter: `import { ${target.importName} } from '${target.importPath}'` };
|
|
14
|
+
});
|
|
15
|
+
const lines = [
|
|
16
|
+
`import { createCodemodTransformer } from '@codegraft/core'`,
|
|
17
|
+
`import codemod from ${JSON.stringify(codemodPath)}`,
|
|
18
|
+
...parts.map((part) => part.splitter).filter((line) => line !== null),
|
|
19
|
+
'',
|
|
20
|
+
...parts.map((part) => `export const ${part.stem} = createCodemodTransformer(${part.expr}, codemod.fn, { namespace: codemod.namespace })`),
|
|
21
|
+
'',
|
|
22
|
+
];
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=entry-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-module.js","sourceRoot":"","sources":["../src/entry-module.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAIjD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,OAAiB;IAChE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAqB,EAAE,CAAA;QACtH,MAAM,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,EAAE,aAAa,MAAM,CAAC,EAAE,mDAAmD,CAAC,CAAA;QACzH,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,MAAM,CAAC,UAAU,YAAY,MAAM,CAAC,UAAU,GAAG,EAAE,CAAA;IAC9H,CAAC,CAAC,CAAA;IACF,MAAM,KAAK,GAAG;QACZ,4DAA4D;QAC5D,uBAAuB,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE;QACpD,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;QACrF,EAAE;QACF,GAAG,KAAK,CAAC,GAAG,CACV,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,IAAI,CAAC,IAAI,+BAA+B,IAAI,CAAC,IAAI,iDAAiD,CAC7H;QACD,EAAE;KACH,CAAA;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { LazyTransformer } from '@codegraft/core';
|
|
2
|
+
type TransformerMap = Record<string, LazyTransformer>;
|
|
3
|
+
export type RunMode = {
|
|
4
|
+
kind: 'dry-run';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'in-place';
|
|
7
|
+
} | {
|
|
8
|
+
kind: 'out-dir';
|
|
9
|
+
dir: string;
|
|
10
|
+
};
|
|
11
|
+
export interface RunResult {
|
|
12
|
+
/** Files whose output differs from their input (written, unless dry-run). */
|
|
13
|
+
transformed: string[];
|
|
14
|
+
/** Files a transformer ran on but left unchanged. */
|
|
15
|
+
unchanged: string[];
|
|
16
|
+
/** Files with no transformer for their extension. */
|
|
17
|
+
skipped: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Apply transformers to a fixed list of files (relative to `cwd`). The pure core of
|
|
21
|
+
* `codegraft run` — no globbing or module loading, so it is directly testable. Each file's
|
|
22
|
+
* extension selects a transformer (lazily `init`-ed once); files with no matching
|
|
23
|
+
* transformer are skipped.
|
|
24
|
+
*/
|
|
25
|
+
export declare function runFiles(opts: {
|
|
26
|
+
files: string[];
|
|
27
|
+
cwd: string;
|
|
28
|
+
transformers: TransformerMap;
|
|
29
|
+
context: Record<string, unknown>;
|
|
30
|
+
mode: RunMode;
|
|
31
|
+
}): Promise<RunResult>;
|
|
32
|
+
/**
|
|
33
|
+
* `codegraft run`: load a codemod, resolve globs, and apply it. The codemod runs **live** — each
|
|
34
|
+
* declared target becomes a lazy transformer via `forTarget`, so the codemod's helpers, imports,
|
|
35
|
+
* and deps work as written, with no build/compile step.
|
|
36
|
+
*/
|
|
37
|
+
export declare function run(opts: {
|
|
38
|
+
patterns: string[];
|
|
39
|
+
cwd: string;
|
|
40
|
+
codemodPath: string;
|
|
41
|
+
context: Record<string, unknown>;
|
|
42
|
+
mode: RunMode;
|
|
43
|
+
}): Promise<RunResult>;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,eAAe,EAA6B,MAAM,iBAAiB,CAAA;AAK5F,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;AAYrD,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpC,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,qDAAqD;IACrD,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IACnC,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,cAAc,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,IAAI,EAAE,OAAO,CAAA;CACd,GAAG,OAAO,CAAC,SAAS,CAAC,CA4BrB;AAaD;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAChC,IAAI,EAAE,OAAO,CAAA;CACd,GAAG,OAAO,CAAC,SAAS,CAAC,CAkBrB"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { glob, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, extname, join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { assert, EXTENSION_GRAMMAR } from '@codegraft/core/internal';
|
|
5
|
+
import { vueSplitter } from '@codegraft/vue';
|
|
6
|
+
// File extension → target stem: the shared grammar map plus SFC splitter stems.
|
|
7
|
+
const EXTENSION_TARGET = { ...EXTENSION_GRAMMAR, vue: 'vue' };
|
|
8
|
+
/**
|
|
9
|
+
* Apply transformers to a fixed list of files (relative to `cwd`). The pure core of
|
|
10
|
+
* `codegraft run` — no globbing or module loading, so it is directly testable. Each file's
|
|
11
|
+
* extension selects a transformer (lazily `init`-ed once); files with no matching
|
|
12
|
+
* transformer are skipped.
|
|
13
|
+
*/
|
|
14
|
+
export async function runFiles(opts) {
|
|
15
|
+
const ready = new Map();
|
|
16
|
+
const result = { transformed: [], unchanged: [], skipped: [] };
|
|
17
|
+
for (const file of opts.files) {
|
|
18
|
+
const stem = EXTENSION_TARGET[extname(file).slice(1)];
|
|
19
|
+
const lazy = stem ? opts.transformers[stem] : undefined;
|
|
20
|
+
if (!lazy) {
|
|
21
|
+
result.skipped.push(file);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
let transformer = ready.get(stem);
|
|
25
|
+
if (!transformer) {
|
|
26
|
+
transformer = await lazy.init();
|
|
27
|
+
ready.set(stem, transformer);
|
|
28
|
+
}
|
|
29
|
+
const absolute = join(opts.cwd, file);
|
|
30
|
+
const source = await readFile(absolute, 'utf8');
|
|
31
|
+
const output = transformer.transform(source, opts.context);
|
|
32
|
+
if (output === source) {
|
|
33
|
+
result.unchanged.push(file);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
result.transformed.push(file);
|
|
37
|
+
await writeOutput(opts.mode, file, absolute, output);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
async function writeOutput(mode, file, absolute, output) {
|
|
42
|
+
if (mode.kind === 'dry-run')
|
|
43
|
+
return;
|
|
44
|
+
if (mode.kind === 'in-place') {
|
|
45
|
+
await writeFile(absolute, output);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const dest = join(mode.dir, file);
|
|
49
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
50
|
+
await writeFile(dest, output);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `codegraft run`: load a codemod, resolve globs, and apply it. The codemod runs **live** — each
|
|
54
|
+
* declared target becomes a lazy transformer via `forTarget`, so the codemod's helpers, imports,
|
|
55
|
+
* and deps work as written, with no build/compile step.
|
|
56
|
+
*/
|
|
57
|
+
export async function run(opts) {
|
|
58
|
+
const mod = (await import(pathToFileURL(opts.codemodPath).href));
|
|
59
|
+
const codemod = mod.default;
|
|
60
|
+
assert(codemod && typeof codemod.forTarget === 'function', `codemod '${opts.codemodPath}' must default-export a defineCodemod result`);
|
|
61
|
+
assert(Array.isArray(mod.targets), `codemod '${opts.codemodPath}' must export a 'targets' array`);
|
|
62
|
+
const transformers = {};
|
|
63
|
+
for (const target of mod.targets) {
|
|
64
|
+
const stem = typeof target === 'string' ? target : target.id;
|
|
65
|
+
transformers[stem] = { target, init: () => codemod.forTarget(target) };
|
|
66
|
+
}
|
|
67
|
+
// The cli applies any codemod to `.vue` too — the splitter feeds it the SFC's zones and the
|
|
68
|
+
// codemod edits only the ones it matches (a JS-family rule touches `<script>`, a CSS rule `<style>`).
|
|
69
|
+
transformers.vue ??= { target: vueSplitter, init: () => codemod.forTarget(vueSplitter) };
|
|
70
|
+
const files = [];
|
|
71
|
+
for await (const match of glob(opts.patterns, { cwd: opts.cwd }))
|
|
72
|
+
files.push(match);
|
|
73
|
+
return runFiles({ files, cwd: opts.cwd, transformers, context: opts.context, mode: opts.mode });
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GrammarId, ZoneSplitter } from '@codegraft/core';
|
|
2
|
+
/**
|
|
3
|
+
* Emit an ES module for a codemod target. A codemod body is param-rooted — everything hangs off
|
|
4
|
+
* the `root`/`context` arguments — so its `.toString()` is self-contained: the generated module
|
|
5
|
+
* imports only `createCodemodTransformer` (plus a splitter), then passes the body verbatim.
|
|
6
|
+
*/
|
|
7
|
+
export declare function serialiseCodemod(target: GrammarId | ZoneSplitter, body: string, namespace?: string): string;
|
|
8
|
+
//# sourceMappingURL=serialise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialise.d.ts","sourceRoot":"","sources":["../src/serialise.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9D;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuB3G"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emit an ES module for a codemod target. A codemod body is param-rooted — everything hangs off
|
|
3
|
+
* the `root`/`context` arguments — so its `.toString()` is self-contained: the generated module
|
|
4
|
+
* imports only `createCodemodTransformer` (plus a splitter), then passes the body verbatim.
|
|
5
|
+
*/
|
|
6
|
+
export function serialiseCodemod(target, body, namespace) {
|
|
7
|
+
const splitter = typeof target === 'string' ? null : target;
|
|
8
|
+
const imports = [`import { createCodemodTransformer } from '@codegraft/core'`];
|
|
9
|
+
let targetExpr;
|
|
10
|
+
if (splitter) {
|
|
11
|
+
if (!splitter.importName || !splitter.importPath) {
|
|
12
|
+
throw new Error(`[codegraft] splitter '${splitter.id}' cannot be serialised: it must declare importName and importPath`);
|
|
13
|
+
}
|
|
14
|
+
imports.push(`import { ${splitter.importName} } from '${splitter.importPath}'`);
|
|
15
|
+
targetExpr = splitter.importName;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
targetExpr = JSON.stringify(target);
|
|
19
|
+
}
|
|
20
|
+
const opts = namespace === undefined ? '' : `, { namespace: ${JSON.stringify(namespace)} }`;
|
|
21
|
+
return [
|
|
22
|
+
'// generated by codegraft build',
|
|
23
|
+
...imports,
|
|
24
|
+
'',
|
|
25
|
+
`export const transform = createCodemodTransformer(${targetExpr}, ${body}${opts})`,
|
|
26
|
+
'',
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=serialise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialise.js","sourceRoot":"","sources":["../src/serialise.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAgC,EAAE,IAAY,EAAE,SAAkB;IACjG,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;IAC3D,MAAM,OAAO,GAAG,CAAC,4DAA4D,CAAC,CAAA;IAC9E,IAAI,UAAkB,CAAA;IACtB,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,CAAC,EAAE,mEAAmE,CACxG,CAAA;QACH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,UAAU,YAAY,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAA;QAC/E,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAA;IAClC,CAAC;SAAM,CAAC;QACN,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAA;IAC3F,OAAO;QACL,iCAAiC;QACjC,GAAG,OAAO;QACV,EAAE;QACF,qDAAqD,UAAU,KAAK,IAAI,GAAG,IAAI,GAAG;QAClF,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codegraft/cli",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Codegraft CLI — apply a codemod over a file tree (codegraft run).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/magne4000/codegraft.git",
|
|
11
|
+
"directory": "packages/cli"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/magne4000/codegraft#readme",
|
|
14
|
+
"bugs": "https://github.com/magne4000/codegraft/issues",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"bin": {
|
|
19
|
+
"codegraft": "./dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.13"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@codegraft/core": "0.1.0-beta.0",
|
|
26
|
+
"@codegraft/vue": "0.1.0-beta.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@codegraft/codemod": "0.1.0-beta.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc -b"
|
|
33
|
+
}
|
|
34
|
+
}
|