@ddt-tools/cli 0.2.0 → 0.2.4
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/dist/advise-tests-YNMKVJCD.js +87 -0
- package/dist/advise-tests-YNMKVJCD.js.map +1 -0
- package/dist/ai-NTNPYEKZ.js +86 -0
- package/dist/ai-NTNPYEKZ.js.map +1 -0
- package/dist/anonymize-LERTWUQO.js +139 -0
- package/dist/anonymize-LERTWUQO.js.map +1 -0
- package/dist/approval-GGZGKIU4.js +73 -0
- package/dist/approval-GGZGKIU4.js.map +1 -0
- package/dist/approval-chain-GWJKZHVU.js +118 -0
- package/dist/approval-chain-GWJKZHVU.js.map +1 -0
- package/dist/audit-log-2PH55BU4.js +159 -0
- package/dist/audit-log-2PH55BU4.js.map +1 -0
- package/dist/backlog-QNXGOUF4.js +76 -0
- package/dist/backlog-QNXGOUF4.js.map +1 -0
- package/dist/bisect-W3XKKRWG.js +111 -0
- package/dist/bisect-W3XKKRWG.js.map +1 -0
- package/dist/bookmarks-XVOGXGMC.js +107 -0
- package/dist/bookmarks-XVOGXGMC.js.map +1 -0
- package/dist/branch-S3I2IJGQ.js +103 -0
- package/dist/branch-S3I2IJGQ.js.map +1 -0
- package/dist/build-MP3JQEFO.js +20 -0
- package/dist/build-MP3JQEFO.js.map +1 -0
- package/dist/catalog-3J3NFNXP.js +137 -0
- package/dist/catalog-3J3NFNXP.js.map +1 -0
- package/dist/changelog-ZQAH3ULB.js +216 -0
- package/dist/changelog-ZQAH3ULB.js.map +1 -0
- package/dist/chunk-2FT6HXKS.js +55 -0
- package/dist/chunk-2FT6HXKS.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-DL3V7UJ2.js +25 -0
- package/dist/chunk-DL3V7UJ2.js.map +1 -0
- package/dist/chunk-VM2H4LAO.js +15 -0
- package/dist/chunk-VM2H4LAO.js.map +1 -0
- package/dist/chunk-XFXG347C.js +40 -0
- package/dist/chunk-XFXG347C.js.map +1 -0
- package/dist/cli.js +499 -19402
- package/dist/cli.js.map +1 -1
- package/dist/compare-P7JOV76O.js +379 -0
- package/dist/compare-P7JOV76O.js.map +1 -0
- package/dist/compare-profiles-H33CXZPD.js +219 -0
- package/dist/compare-profiles-H33CXZPD.js.map +1 -0
- package/dist/completion-ZSNCQKJ2.js +89 -0
- package/dist/completion-ZSNCQKJ2.js.map +1 -0
- package/dist/connection-CDGVEFUC.js +148 -0
- package/dist/connection-CDGVEFUC.js.map +1 -0
- package/dist/cost-estimate-S2MKHT2H.js +321 -0
- package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
- package/dist/data-compare-46ZI7KHL.js +128 -0
- package/dist/data-compare-46ZI7KHL.js.map +1 -0
- package/dist/data-fit-WGEPLD5S.js +127 -0
- package/dist/data-fit-WGEPLD5S.js.map +1 -0
- package/dist/deploy-status-4H5KJFRC.js +58 -0
- package/dist/deploy-status-4H5KJFRC.js.map +1 -0
- package/dist/design-ILX3ZSWW.js +135 -0
- package/dist/design-ILX3ZSWW.js.map +1 -0
- package/dist/diagnose-WPUL67E4.js +150 -0
- package/dist/diagnose-WPUL67E4.js.map +1 -0
- package/dist/discover-DEO2R5T6.js +78 -0
- package/dist/discover-DEO2R5T6.js.map +1 -0
- package/dist/docs-QNY3MUVO.js +183 -0
- package/dist/docs-QNY3MUVO.js.map +1 -0
- package/dist/drift-FDRNPWQA.js +233 -0
- package/dist/drift-FDRNPWQA.js.map +1 -0
- package/dist/drift-gate-6BWWWMHW.js +103 -0
- package/dist/drift-gate-6BWWWMHW.js.map +1 -0
- package/dist/error-lookup-4R3Y4RBC.js +56 -0
- package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
- package/dist/errorReporting-3LPE2IJY.js +109 -0
- package/dist/errorReporting-3LPE2IJY.js.map +1 -0
- package/dist/exec-JOLH5LPT.js +122 -0
- package/dist/exec-JOLH5LPT.js.map +1 -0
- package/dist/explain-NS26WE2Y.js +189 -0
- package/dist/explain-NS26WE2Y.js.map +1 -0
- package/dist/explorer-GSYYYOAL.js +58 -0
- package/dist/explorer-GSYYYOAL.js.map +1 -0
- package/dist/extract-4LWEZG4O.js +152 -0
- package/dist/extract-4LWEZG4O.js.map +1 -0
- package/dist/features-KQV4OFIZ.js +54 -0
- package/dist/features-KQV4OFIZ.js.map +1 -0
- package/dist/feedback-CBLGXUEG.js +158 -0
- package/dist/feedback-CBLGXUEG.js.map +1 -0
- package/dist/find-SMXRCZ76.js +176 -0
- package/dist/find-SMXRCZ76.js.map +1 -0
- package/dist/format-HMGG6MY3.js +277 -0
- package/dist/format-HMGG6MY3.js.map +1 -0
- package/dist/generate-W7VLBDLI.js +160 -0
- package/dist/generate-W7VLBDLI.js.map +1 -0
- package/dist/graph-YYL5UYCJ.js +168 -0
- package/dist/graph-YYL5UYCJ.js.map +1 -0
- package/dist/history-GDRFP4PG.js +184 -0
- package/dist/history-GDRFP4PG.js.map +1 -0
- package/dist/hosts-DRFZTMIJ.js +45 -0
- package/dist/hosts-DRFZTMIJ.js.map +1 -0
- package/dist/impact-A4NU6CB2.js +63 -0
- package/dist/impact-A4NU6CB2.js.map +1 -0
- package/dist/import-2RNYDL4E.js +79 -0
- package/dist/import-2RNYDL4E.js.map +1 -0
- package/dist/index.cjs +11 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/init-EAOGNGXI.js +54 -0
- package/dist/init-EAOGNGXI.js.map +1 -0
- package/dist/install-hooks-G3Y5LVXK.js +109 -0
- package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
- package/dist/license-Z5YSC7XQ.js +43 -0
- package/dist/license-Z5YSC7XQ.js.map +1 -0
- package/dist/lineage-C5CGVP36.js +555 -0
- package/dist/lineage-C5CGVP36.js.map +1 -0
- package/dist/lint-AQFPZ3WG.js +144 -0
- package/dist/lint-AQFPZ3WG.js.map +1 -0
- package/dist/mcp-F7FND5X7.js +343 -0
- package/dist/mcp-F7FND5X7.js.map +1 -0
- package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
- package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
- package/dist/migrate-platform-E7VZFPO5.js +91 -0
- package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
- package/dist/optimize-WUJ5ZN5Y.js +109 -0
- package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
- package/dist/perf-UULZSREY.js +200 -0
- package/dist/perf-UULZSREY.js.map +1 -0
- package/dist/pii-QHU32VML.js +146 -0
- package/dist/pii-QHU32VML.js.map +1 -0
- package/dist/pilot-BR6GVK32.js +29 -0
- package/dist/pilot-BR6GVK32.js.map +1 -0
- package/dist/pr-comment-2FOA3EXG.js +81 -0
- package/dist/pr-comment-2FOA3EXG.js.map +1 -0
- package/dist/preview-XNY422OU.js +46 -0
- package/dist/preview-XNY422OU.js.map +1 -0
- package/dist/profile-SQTBNKYS.js +98 -0
- package/dist/profile-SQTBNKYS.js.map +1 -0
- package/dist/promote-FSGUPIPD.js +417 -0
- package/dist/promote-FSGUPIPD.js.map +1 -0
- package/dist/publish-AYCRMCE2.js +739 -0
- package/dist/publish-AYCRMCE2.js.map +1 -0
- package/dist/purge-Y5IOTXKA.js +56 -0
- package/dist/purge-Y5IOTXKA.js.map +1 -0
- package/dist/query-log-SDDGMJLJ.js +112 -0
- package/dist/query-log-SDDGMJLJ.js.map +1 -0
- package/dist/refactor-TC7S43F2.js +5809 -0
- package/dist/refactor-TC7S43F2.js.map +1 -0
- package/dist/refresh-MDJYOYV5.js +39 -0
- package/dist/refresh-MDJYOYV5.js.map +1 -0
- package/dist/replay-E4664A5K.js +118 -0
- package/dist/replay-E4664A5K.js.map +1 -0
- package/dist/revert-QWQWCJJB.js +111 -0
- package/dist/revert-QWQWCJJB.js.map +1 -0
- package/dist/review-7CAVLD67.js +164 -0
- package/dist/review-7CAVLD67.js.map +1 -0
- package/dist/rollback-suggest-C6D5YFCA.js +79 -0
- package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
- package/dist/safer-alternative-QR4QEFUV.js +84 -0
- package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
- package/dist/safety-OFWUFLK4.js +165 -0
- package/dist/safety-OFWUFLK4.js.map +1 -0
- package/dist/savings-MEBE4TXI.js +95 -0
- package/dist/savings-MEBE4TXI.js.map +1 -0
- package/dist/scan-secrets-XCUBMLHL.js +54 -0
- package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
- package/dist/schema-7JZIG6QR.js +447 -0
- package/dist/schema-7JZIG6QR.js.map +1 -0
- package/dist/script-BMYVBHFR.js +167 -0
- package/dist/script-BMYVBHFR.js.map +1 -0
- package/dist/search-TA3C3AZT.js +151 -0
- package/dist/search-TA3C3AZT.js.map +1 -0
- package/dist/seed-W4Q3L2IU.js +101 -0
- package/dist/seed-W4Q3L2IU.js.map +1 -0
- package/dist/sketch-6B2V6FJV.js +83 -0
- package/dist/sketch-6B2V6FJV.js.map +1 -0
- package/dist/snapshot-YMVS322L.js +171 -0
- package/dist/snapshot-YMVS322L.js.map +1 -0
- package/dist/snippets-EVTN63OU.js +74 -0
- package/dist/snippets-EVTN63OU.js.map +1 -0
- package/dist/standards-FGJW3CQL.js +238 -0
- package/dist/standards-FGJW3CQL.js.map +1 -0
- package/dist/suggest-V3LVIFZ5.js +44 -0
- package/dist/suggest-V3LVIFZ5.js.map +1 -0
- package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
- package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
- package/dist/suite-YTQ3CNX5.js +85 -0
- package/dist/suite-YTQ3CNX5.js.map +1 -0
- package/dist/telemetry-KOIY3NEQ.js +90 -0
- package/dist/telemetry-KOIY3NEQ.js.map +1 -0
- package/dist/template-MUJ6X6LN.js +396 -0
- package/dist/template-MUJ6X6LN.js.map +1 -0
- package/dist/test-XFSQHR2S.js +169 -0
- package/dist/test-XFSQHR2S.js.map +1 -0
- package/dist/trial-GFTGYCR3.js +31 -0
- package/dist/trial-GFTGYCR3.js.map +1 -0
- package/dist/validate-LFDEZFFH.js +107 -0
- package/dist/validate-LFDEZFFH.js.map +1 -0
- package/dist/verify-KRDYOJCR.js +76 -0
- package/dist/verify-KRDYOJCR.js.map +1 -0
- package/dist/watch-FSG23RR3.js +80 -0
- package/dist/watch-FSG23RR3.js.map +1 -0
- package/dist/xcompare-U4TXTTIR.js +87 -0
- package/dist/xcompare-U4TXTTIR.js.map +1 -0
- package/package.json +2 -2
- package/dist/cli.cjs +0 -19298
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/format.ts
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { loadProject, discoverObjectFiles } from "@ddt-tools/core";
|
|
8
|
+
import {
|
|
9
|
+
FORMAT_STYLE_PRESET_NAMES,
|
|
10
|
+
format as formatV2,
|
|
11
|
+
getFormatPreset,
|
|
12
|
+
isFormatStylePreset,
|
|
13
|
+
mergeFormatStyle
|
|
14
|
+
} from "@ddt-tools/core/format";
|
|
15
|
+
var RESERVED = /* @__PURE__ */ new Set([
|
|
16
|
+
"CREATE",
|
|
17
|
+
"OR",
|
|
18
|
+
"REPLACE",
|
|
19
|
+
"REFRESH",
|
|
20
|
+
"STREAMING",
|
|
21
|
+
"MATERIALIZED",
|
|
22
|
+
"TEMPORARY",
|
|
23
|
+
"EXTERNAL",
|
|
24
|
+
"MANAGED",
|
|
25
|
+
"TABLE",
|
|
26
|
+
"VIEW",
|
|
27
|
+
"CATALOG",
|
|
28
|
+
"SCHEMA",
|
|
29
|
+
"FUNCTION",
|
|
30
|
+
"VOLUME",
|
|
31
|
+
"PIPELINE",
|
|
32
|
+
"CONNECTION",
|
|
33
|
+
"SHARE",
|
|
34
|
+
"RECIPIENT",
|
|
35
|
+
"WAREHOUSE",
|
|
36
|
+
"CLUSTER",
|
|
37
|
+
"ALTER",
|
|
38
|
+
"DROP",
|
|
39
|
+
"TRUNCATE",
|
|
40
|
+
"COMMENT",
|
|
41
|
+
"GRANT",
|
|
42
|
+
"REVOKE",
|
|
43
|
+
"USE",
|
|
44
|
+
"SELECT",
|
|
45
|
+
"FROM",
|
|
46
|
+
"WHERE",
|
|
47
|
+
"GROUP",
|
|
48
|
+
"BY",
|
|
49
|
+
"ORDER",
|
|
50
|
+
"LIMIT",
|
|
51
|
+
"OFFSET",
|
|
52
|
+
"JOIN",
|
|
53
|
+
"INNER",
|
|
54
|
+
"LEFT",
|
|
55
|
+
"RIGHT",
|
|
56
|
+
"OUTER",
|
|
57
|
+
"CROSS",
|
|
58
|
+
"ON",
|
|
59
|
+
"AS",
|
|
60
|
+
"AND",
|
|
61
|
+
"OR",
|
|
62
|
+
"NOT",
|
|
63
|
+
"IN",
|
|
64
|
+
"IS",
|
|
65
|
+
"NULL",
|
|
66
|
+
"TRUE",
|
|
67
|
+
"FALSE",
|
|
68
|
+
"BETWEEN",
|
|
69
|
+
"LIKE",
|
|
70
|
+
"EXISTS",
|
|
71
|
+
"INSERT",
|
|
72
|
+
"INTO",
|
|
73
|
+
"UPDATE",
|
|
74
|
+
"SET",
|
|
75
|
+
"DELETE",
|
|
76
|
+
"MERGE",
|
|
77
|
+
"USING",
|
|
78
|
+
"WHEN",
|
|
79
|
+
"MATCHED",
|
|
80
|
+
"WITH",
|
|
81
|
+
"CASE",
|
|
82
|
+
"THEN",
|
|
83
|
+
"ELSE",
|
|
84
|
+
"END",
|
|
85
|
+
"UNION",
|
|
86
|
+
"ALL",
|
|
87
|
+
"DISTINCT",
|
|
88
|
+
"PARTITIONED",
|
|
89
|
+
"CLUSTERED",
|
|
90
|
+
"TBLPROPERTIES",
|
|
91
|
+
"LOCATION",
|
|
92
|
+
"USING",
|
|
93
|
+
"OPTIONS",
|
|
94
|
+
"PRIMARY",
|
|
95
|
+
"FOREIGN",
|
|
96
|
+
"KEY",
|
|
97
|
+
"CONSTRAINT",
|
|
98
|
+
"REFERENCES",
|
|
99
|
+
"UNIQUE",
|
|
100
|
+
"CHECK",
|
|
101
|
+
"IF",
|
|
102
|
+
"CASCADE",
|
|
103
|
+
"RESTRICT",
|
|
104
|
+
// Common Databricks-side types
|
|
105
|
+
"BIGINT",
|
|
106
|
+
"INT",
|
|
107
|
+
"SMALLINT",
|
|
108
|
+
"TINYINT",
|
|
109
|
+
"FLOAT",
|
|
110
|
+
"DOUBLE",
|
|
111
|
+
"DECIMAL",
|
|
112
|
+
"BOOLEAN",
|
|
113
|
+
"STRING",
|
|
114
|
+
"DATE",
|
|
115
|
+
"TIMESTAMP",
|
|
116
|
+
"TIMESTAMP_NTZ",
|
|
117
|
+
"BINARY",
|
|
118
|
+
"ARRAY",
|
|
119
|
+
"MAP",
|
|
120
|
+
"STRUCT",
|
|
121
|
+
"VARIANT",
|
|
122
|
+
"INTERVAL"
|
|
123
|
+
]);
|
|
124
|
+
function formatCommand() {
|
|
125
|
+
const cmd = new Command("format");
|
|
126
|
+
cmd.description(
|
|
127
|
+
"Format every .sql under a .ddtproj \u2014 uppercase reserved keywords + tidy whitespace."
|
|
128
|
+
).requiredOption("-p, --project <path>", "Path to the .ddtproj file.").option("--check", "Report files that would change without writing.", false).option(
|
|
129
|
+
"--engine <v>",
|
|
130
|
+
'Formatter engine: "legacy" (default) | "v2" (new tokenizer-based engine with refuse-on-invalid).',
|
|
131
|
+
"legacy"
|
|
132
|
+
).option(
|
|
133
|
+
"--dbt",
|
|
134
|
+
"Force dbt-mode on (v2 engine only): pre-mask {{ ... }} / {% ... %} Jinja blocks so they survive byte-for-byte across the format pass. Default: auto-detect."
|
|
135
|
+
).option(
|
|
136
|
+
"--no-dbt",
|
|
137
|
+
"Force dbt-mode off (v2 engine only): feed Jinja text directly to the formatter (almost always wrong on dbt models)."
|
|
138
|
+
).option(
|
|
139
|
+
"--style <preset>",
|
|
140
|
+
`Apply a named layout preset before any project-level overrides (v2 engine only): ${FORMAT_STYLE_PRESET_NAMES.join(" | ")}. compact = single-line where possible (120-col, 0 blank lines, sameLine closers); expanded = each clause on its own line (100-col, 1 blank line, aligned closers); wide = expanded with maxLineWidth 140 for ultrawide terminals.`
|
|
141
|
+
).action(async (opts) => {
|
|
142
|
+
const engine = String(opts.engine ?? "legacy").toLowerCase();
|
|
143
|
+
if (engine !== "legacy" && engine !== "v2") {
|
|
144
|
+
throw new Error(`Unknown --engine: ${opts.engine}. Use "legacy" or "v2".`);
|
|
145
|
+
}
|
|
146
|
+
const dbtMode = opts.dbt === true ? "on" : opts.dbt === false ? "off" : void 0;
|
|
147
|
+
let preset;
|
|
148
|
+
if (opts.style) {
|
|
149
|
+
const styleName = String(opts.style).toLowerCase();
|
|
150
|
+
if (!isFormatStylePreset(styleName)) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`--style must be one of: ${FORMAT_STYLE_PRESET_NAMES.join(", ")}. Got '${opts.style}'.`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
preset = getFormatPreset(styleName);
|
|
156
|
+
}
|
|
157
|
+
const dbtOverride = dbtMode ? { experimental: { dbtMode } } : {};
|
|
158
|
+
const v2Style = preset || dbtMode ? mergeFormatStyle(preset, dbtOverride) : void 0;
|
|
159
|
+
const loaded = await loadProject(String(opts.project));
|
|
160
|
+
const files = await discoverObjectFiles(loaded);
|
|
161
|
+
const sqlFiles = files.filter((f) => f.endsWith(".sql"));
|
|
162
|
+
let changed = 0;
|
|
163
|
+
let refused = 0;
|
|
164
|
+
for (const f of sqlFiles) {
|
|
165
|
+
const before = await fs.readFile(f, "utf8");
|
|
166
|
+
const rel = path.relative(loaded.rootDir, f);
|
|
167
|
+
if (engine === "v2") {
|
|
168
|
+
const result = formatV2(before, v2Style);
|
|
169
|
+
if (result.refused) {
|
|
170
|
+
const errs = result.diagnostics.filter(
|
|
171
|
+
(d) => d.severity === "ERROR"
|
|
172
|
+
);
|
|
173
|
+
console.error(`refused ${rel} \u2014 ${errs.length} error(s):`);
|
|
174
|
+
for (const d of errs) {
|
|
175
|
+
console.error(` L${d.line}:${d.column} [${d.code}] ${d.message}`);
|
|
176
|
+
}
|
|
177
|
+
refused++;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (result.changed) {
|
|
181
|
+
changed++;
|
|
182
|
+
if (opts.check) {
|
|
183
|
+
console.log(`would change ${rel}`);
|
|
184
|
+
} else {
|
|
185
|
+
await fs.writeFile(f, result.output, "utf8");
|
|
186
|
+
console.log(`formatted ${rel}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const after = formatSql(before);
|
|
192
|
+
if (after !== before) {
|
|
193
|
+
changed++;
|
|
194
|
+
if (opts.check) {
|
|
195
|
+
console.log(`would change ${rel}`);
|
|
196
|
+
} else {
|
|
197
|
+
await fs.writeFile(f, after, "utf8");
|
|
198
|
+
console.log(`formatted ${rel}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
console.log("");
|
|
203
|
+
const verb = opts.check ? "would change" : "changed";
|
|
204
|
+
const refusedNote = refused > 0 ? ` (${refused} refused on errors)` : "";
|
|
205
|
+
console.log(`Summary: ${changed} of ${sqlFiles.length} file(s) ${verb}${refusedNote}.`);
|
|
206
|
+
if (opts.check && changed > 0) process.exitCode = 1;
|
|
207
|
+
if (refused > 0) process.exitCode = 1;
|
|
208
|
+
});
|
|
209
|
+
return cmd;
|
|
210
|
+
}
|
|
211
|
+
function formatSql(sql) {
|
|
212
|
+
const out = [];
|
|
213
|
+
let i = 0;
|
|
214
|
+
const n = sql.length;
|
|
215
|
+
while (i < n) {
|
|
216
|
+
const c = sql[i];
|
|
217
|
+
const next = sql[i + 1];
|
|
218
|
+
if (c === "-" && next === "-") {
|
|
219
|
+
while (i < n && sql[i] !== "\n") {
|
|
220
|
+
out.push(sql[i]);
|
|
221
|
+
i++;
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (c === "/" && next === "*") {
|
|
226
|
+
out.push("/*");
|
|
227
|
+
i += 2;
|
|
228
|
+
while (i < n && !(sql[i] === "*" && sql[i + 1] === "/")) {
|
|
229
|
+
out.push(sql[i]);
|
|
230
|
+
i++;
|
|
231
|
+
}
|
|
232
|
+
if (i < n) {
|
|
233
|
+
out.push("*/");
|
|
234
|
+
i += 2;
|
|
235
|
+
}
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (c === "'" || c === '"' || c === "`") {
|
|
239
|
+
const quote = c;
|
|
240
|
+
out.push(quote);
|
|
241
|
+
i++;
|
|
242
|
+
while (i < n) {
|
|
243
|
+
const cc = sql[i];
|
|
244
|
+
if (cc === quote && sql[i + 1] === quote) {
|
|
245
|
+
out.push(cc);
|
|
246
|
+
out.push(sql[i + 1] ?? "");
|
|
247
|
+
i += 2;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
out.push(cc);
|
|
251
|
+
i++;
|
|
252
|
+
if (cc === quote) break;
|
|
253
|
+
}
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (c && /[A-Za-z_]/.test(c)) {
|
|
257
|
+
let j = i;
|
|
258
|
+
while (j < n && /[A-Za-z0-9_]/.test(sql[j])) j++;
|
|
259
|
+
const word = sql.slice(i, j);
|
|
260
|
+
out.push(RESERVED.has(word.toUpperCase()) ? word.toUpperCase() : word);
|
|
261
|
+
i = j;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (c !== void 0) out.push(c);
|
|
265
|
+
i++;
|
|
266
|
+
}
|
|
267
|
+
let result = out.join("");
|
|
268
|
+
result = result.split("\n").map((l) => l.replace(/[ \t]+$/g, "")).join("\n");
|
|
269
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
270
|
+
result = result.replace(/\n*$/, "\n");
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
formatCommand,
|
|
275
|
+
formatSql
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=format-HMGG6MY3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/format.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { loadProject, discoverObjectFiles } from '@ddt-tools/core';\nimport {\n FORMAT_STYLE_PRESET_NAMES,\n format as formatV2,\n getFormatPreset,\n isFormatStylePreset,\n mergeFormatStyle,\n} from '@ddt-tools/core/format';\nimport type { FormatDiagnostic, FormatStyle } from '@ddt-tools/core/format';\n\n/**\n * `ddt format` — opinionated SQL formatter for project files.\n *\n * The formatter is intentionally simple and platform-agnostic:\n * - Uppercase reserved keywords (CREATE / TABLE / VIEW / SELECT / ...).\n * - Trim trailing whitespace.\n * - Collapse 3+ blank lines to 2.\n * - Ensure file ends with exactly one newline.\n *\n * It does NOT try to be a full SQL pretty-printer (indenting clauses,\n * aligning column lists). Real prettifying requires a dialect-aware\n * parser; users can wire `sql-formatter` in their editor for that.\n */\nconst RESERVED = new Set<string>([\n 'CREATE',\n 'OR',\n 'REPLACE',\n 'REFRESH',\n 'STREAMING',\n 'MATERIALIZED',\n 'TEMPORARY',\n 'EXTERNAL',\n 'MANAGED',\n 'TABLE',\n 'VIEW',\n 'CATALOG',\n 'SCHEMA',\n 'FUNCTION',\n 'VOLUME',\n 'PIPELINE',\n 'CONNECTION',\n 'SHARE',\n 'RECIPIENT',\n 'WAREHOUSE',\n 'CLUSTER',\n 'ALTER',\n 'DROP',\n 'TRUNCATE',\n 'COMMENT',\n 'GRANT',\n 'REVOKE',\n 'USE',\n 'SELECT',\n 'FROM',\n 'WHERE',\n 'GROUP',\n 'BY',\n 'ORDER',\n 'LIMIT',\n 'OFFSET',\n 'JOIN',\n 'INNER',\n 'LEFT',\n 'RIGHT',\n 'OUTER',\n 'CROSS',\n 'ON',\n 'AS',\n 'AND',\n 'OR',\n 'NOT',\n 'IN',\n 'IS',\n 'NULL',\n 'TRUE',\n 'FALSE',\n 'BETWEEN',\n 'LIKE',\n 'EXISTS',\n 'INSERT',\n 'INTO',\n 'UPDATE',\n 'SET',\n 'DELETE',\n 'MERGE',\n 'USING',\n 'WHEN',\n 'MATCHED',\n 'WITH',\n 'CASE',\n 'THEN',\n 'ELSE',\n 'END',\n 'UNION',\n 'ALL',\n 'DISTINCT',\n 'PARTITIONED',\n 'CLUSTERED',\n 'TBLPROPERTIES',\n 'LOCATION',\n 'USING',\n 'OPTIONS',\n 'PRIMARY',\n 'FOREIGN',\n 'KEY',\n 'CONSTRAINT',\n 'REFERENCES',\n 'UNIQUE',\n 'CHECK',\n 'IF',\n 'CASCADE',\n 'RESTRICT',\n // Common Databricks-side types\n 'BIGINT',\n 'INT',\n 'SMALLINT',\n 'TINYINT',\n 'FLOAT',\n 'DOUBLE',\n 'DECIMAL',\n 'BOOLEAN',\n 'STRING',\n 'DATE',\n 'TIMESTAMP',\n 'TIMESTAMP_NTZ',\n 'BINARY',\n 'ARRAY',\n 'MAP',\n 'STRUCT',\n 'VARIANT',\n 'INTERVAL',\n]);\n\nexport function formatCommand(): Command {\n const cmd = new Command('format');\n cmd\n .description(\n 'Format every .sql under a .ddtproj — uppercase reserved keywords + tidy whitespace.',\n )\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .option('--check', 'Report files that would change without writing.', false)\n .option(\n '--engine <v>',\n 'Formatter engine: \"legacy\" (default) | \"v2\" (new tokenizer-based engine with refuse-on-invalid).',\n 'legacy',\n )\n .option(\n '--dbt',\n 'Force dbt-mode on (v2 engine only): pre-mask {{ ... }} / {% ... %} Jinja blocks so they survive byte-for-byte across the format pass. Default: auto-detect.',\n )\n .option(\n '--no-dbt',\n 'Force dbt-mode off (v2 engine only): feed Jinja text directly to the formatter (almost always wrong on dbt models).',\n )\n .option(\n '--style <preset>',\n `Apply a named layout preset before any project-level overrides (v2 engine only): ${FORMAT_STYLE_PRESET_NAMES.join(' | ')}. compact = single-line where possible (120-col, 0 blank lines, sameLine closers); expanded = each clause on its own line (100-col, 1 blank line, aligned closers); wide = expanded with maxLineWidth 140 for ultrawide terminals.`,\n )\n .action(async (opts) => {\n const engine = String(opts.engine ?? 'legacy').toLowerCase();\n if (engine !== 'legacy' && engine !== 'v2') {\n throw new Error(`Unknown --engine: ${opts.engine}. Use \"legacy\" or \"v2\".`);\n }\n // DBTC.4 — --dbt / --no-dbt resolves to FormatStyle.experimental.dbtMode\n // (v2 engine only). Commander's --no-dbt yields opts.dbt === false.\n const dbtMode: 'auto' | 'on' | 'off' | undefined =\n opts.dbt === true ? 'on' : opts.dbt === false ? 'off' : undefined;\n let preset: FormatStyle | undefined;\n if (opts.style) {\n const styleName = String(opts.style).toLowerCase();\n if (!isFormatStylePreset(styleName)) {\n throw new Error(\n `--style must be one of: ${FORMAT_STYLE_PRESET_NAMES.join(', ')}. Got '${opts.style}'.`,\n );\n }\n preset = getFormatPreset(styleName);\n }\n const dbtOverride: FormatStyle = dbtMode ? { experimental: { dbtMode } } : {};\n const v2Style: FormatStyle | undefined =\n preset || dbtMode ? mergeFormatStyle(preset, dbtOverride) : undefined;\n const loaded = await loadProject(String(opts.project));\n const files = await discoverObjectFiles(loaded);\n const sqlFiles = files.filter((f) => f.endsWith('.sql'));\n let changed = 0;\n let refused = 0;\n for (const f of sqlFiles) {\n const before = await fs.readFile(f, 'utf8');\n const rel = path.relative(loaded.rootDir, f);\n\n if (engine === 'v2') {\n const result = formatV2(before, v2Style);\n if (result.refused) {\n const errs: FormatDiagnostic[] = result.diagnostics.filter(\n (d) => d.severity === 'ERROR',\n );\n console.error(`refused ${rel} — ${errs.length} error(s):`);\n for (const d of errs) {\n console.error(` L${d.line}:${d.column} [${d.code}] ${d.message}`);\n }\n refused++;\n continue;\n }\n if (result.changed) {\n changed++;\n if (opts.check) {\n console.log(`would change ${rel}`);\n } else {\n await fs.writeFile(f, result.output, 'utf8');\n console.log(`formatted ${rel}`);\n }\n }\n continue;\n }\n\n // Legacy engine.\n const after = formatSql(before);\n if (after !== before) {\n changed++;\n if (opts.check) {\n console.log(`would change ${rel}`);\n } else {\n await fs.writeFile(f, after, 'utf8');\n console.log(`formatted ${rel}`);\n }\n }\n }\n console.log('');\n const verb = opts.check ? 'would change' : 'changed';\n const refusedNote = refused > 0 ? ` (${refused} refused on errors)` : '';\n console.log(`Summary: ${changed} of ${sqlFiles.length} file(s) ${verb}${refusedNote}.`);\n if (opts.check && changed > 0) process.exitCode = 1;\n if (refused > 0) process.exitCode = 1;\n });\n return cmd;\n}\n\n/**\n * Token-aware reformat. Skips strings + comments + quoted identifiers,\n * upcases reserved keywords elsewhere, then normalizes whitespace.\n */\nexport function formatSql(sql: string): string {\n const out: string[] = [];\n let i = 0;\n const n = sql.length;\n while (i < n) {\n const c = sql[i];\n const next = sql[i + 1];\n\n // Comments.\n if (c === '-' && next === '-') {\n while (i < n && sql[i] !== '\\n') {\n out.push(sql[i]!);\n i++;\n }\n continue;\n }\n if (c === '/' && next === '*') {\n out.push('/*');\n i += 2;\n while (i < n && !(sql[i] === '*' && sql[i + 1] === '/')) {\n out.push(sql[i]!);\n i++;\n }\n if (i < n) {\n out.push('*/');\n i += 2;\n }\n continue;\n }\n\n // String literals.\n if (c === \"'\" || c === '\"' || c === '`') {\n const quote = c;\n out.push(quote);\n i++;\n while (i < n) {\n const cc = sql[i]!;\n if (cc === quote && sql[i + 1] === quote) {\n out.push(cc);\n out.push(sql[i + 1] ?? '');\n i += 2;\n continue;\n }\n out.push(cc);\n i++;\n if (cc === quote) break;\n }\n continue;\n }\n\n // Identifier-ish token.\n if (c && /[A-Za-z_]/.test(c)) {\n let j = i;\n while (j < n && /[A-Za-z0-9_]/.test(sql[j]!)) j++;\n const word = sql.slice(i, j);\n out.push(RESERVED.has(word.toUpperCase()) ? word.toUpperCase() : word);\n i = j;\n continue;\n }\n\n if (c !== undefined) out.push(c);\n i++;\n }\n let result = out.join('');\n // Trim trailing whitespace on each line.\n result = result\n .split('\\n')\n .map((l) => l.replace(/[ \\t]+$/g, ''))\n .join('\\n');\n // Collapse 3+ blank lines to 2.\n result = result.replace(/\\n{3,}/g, '\\n\\n');\n // Ensure exactly one trailing newline.\n result = result.replace(/\\n*$/, '\\n');\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/generate.ts
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { generate } from "@ddt-tools/core";
|
|
8
|
+
function notebookSubcommand() {
|
|
9
|
+
return new Command("notebook").description("Emit a Databricks PySpark ingest notebook from a JSON sample file.").requiredOption("--from-json <path>", "Path to a JSON data file (array of records or NDJSON).").requiredOption("--target <fqn>", "Target table FQN (catalog.schema.table).").requiredOption("--source <path>", "Source path for the read (S3 / DBFS / volume path).").option("--runtime <ver>", "Databricks Runtime version tag (e.g. dbr15.4-lts).", "dbr14.3-lts").option("--merge-schema", "Emit mergeSchema option for schema evolution.", false).option("--write-mode <mode>", "append | overwrite (default append).", "append").option("--out <dir>", "Write .ipynb + .py to this directory instead of stdout.").action(async (opts) => {
|
|
10
|
+
const rows = await readJsonRows(opts.fromJson);
|
|
11
|
+
const schema = await generate.inferTypes(rows);
|
|
12
|
+
const result = generate.emitIngestNotebook(schema, {
|
|
13
|
+
targetFqn: opts.target,
|
|
14
|
+
sourcePath: opts.source,
|
|
15
|
+
runtime: opts.runtime,
|
|
16
|
+
mergeSchema: Boolean(opts.mergeSchema),
|
|
17
|
+
writeMode: opts.writeMode === "overwrite" ? "overwrite" : "append"
|
|
18
|
+
});
|
|
19
|
+
if (opts.out) {
|
|
20
|
+
await fs.mkdir(path.resolve(opts.out), { recursive: true });
|
|
21
|
+
const stem = sanitizeFileStem(opts.target);
|
|
22
|
+
await fs.writeFile(
|
|
23
|
+
path.join(path.resolve(opts.out), `ingest_${stem}.ipynb`),
|
|
24
|
+
JSON.stringify(result.ipynb, null, 2) + "\n",
|
|
25
|
+
"utf8"
|
|
26
|
+
);
|
|
27
|
+
await fs.writeFile(
|
|
28
|
+
path.join(path.resolve(opts.out), `ingest_${stem}.py`),
|
|
29
|
+
result.py,
|
|
30
|
+
"utf8"
|
|
31
|
+
);
|
|
32
|
+
} else {
|
|
33
|
+
process.stdout.write(result.py);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function scdSubcommand() {
|
|
38
|
+
return new Command("scd").description("Emit SCD PySpark + SparkSQL merge code for a given table and strategy.").requiredOption("--table <fqn>", "Target table FQN (catalog.schema.table).").requiredOption("--natural-key <cols>", "Comma-separated natural key columns.").option("--type <n>", "SCD type (1 | 2, default 2).", "2").option("--effective-from <col>", "Effective-from timestamp column.", "effective_from").option("--effective-to <col>", "Effective-to timestamp column.", "effective_to").option("--is-current <col>", "Is-current boolean column (optional).").option("--hash-cols <cols>", "Comma-separated columns for hash-based change detection.").option("--surrogate-key <col>", "Surrogate key column (SCD-2 only).", "id").option(
|
|
39
|
+
"--on-schema-change <action>",
|
|
40
|
+
"add-column | fail | rebuild (default add-column).",
|
|
41
|
+
"add-column"
|
|
42
|
+
).option("--out <dir>", "Write .py + .sql files to this directory instead of stdout.").action(async (opts) => {
|
|
43
|
+
const naturalKey = String(opts.naturalKey).split(",").map((s) => s.trim()).filter(Boolean);
|
|
44
|
+
if (naturalKey.length === 0) {
|
|
45
|
+
throw new Error("--natural-key must list at least one column name.");
|
|
46
|
+
}
|
|
47
|
+
const scdType = parseInt(opts.type, 10);
|
|
48
|
+
if (scdType !== 1 && scdType !== 2) {
|
|
49
|
+
throw new Error(`--type must be 1 or 2 (got '${opts.type}').`);
|
|
50
|
+
}
|
|
51
|
+
const hashCols = opts.hashCols ? String(opts.hashCols).split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
52
|
+
const strategy = {
|
|
53
|
+
type: scdType,
|
|
54
|
+
naturalKey,
|
|
55
|
+
surrogateKeyColumn: scdType === 2 ? opts.surrogateKey : void 0,
|
|
56
|
+
effectiveFromColumn: opts.effectiveFrom,
|
|
57
|
+
effectiveToColumn: opts.effectiveTo,
|
|
58
|
+
isCurrentColumn: opts.isCurrent,
|
|
59
|
+
hashColumns: hashCols,
|
|
60
|
+
onSchemaChange: opts.onSchemaChange
|
|
61
|
+
};
|
|
62
|
+
const result = generate.emitScdPyspark(strategy, {
|
|
63
|
+
targetFqn: opts.table,
|
|
64
|
+
sourceFqn: `staging.stg_${sanitizeFileStem(opts.table)}`,
|
|
65
|
+
projectionColumns: naturalKey
|
|
66
|
+
});
|
|
67
|
+
if (opts.out) {
|
|
68
|
+
const stem = sanitizeFileStem(opts.table);
|
|
69
|
+
await fs.mkdir(path.resolve(opts.out), { recursive: true });
|
|
70
|
+
await fs.writeFile(
|
|
71
|
+
path.join(path.resolve(opts.out), `scd_${stem}.py`),
|
|
72
|
+
result.pyspark,
|
|
73
|
+
"utf8"
|
|
74
|
+
);
|
|
75
|
+
await fs.writeFile(
|
|
76
|
+
path.join(path.resolve(opts.out), `scd_${stem}.sql`),
|
|
77
|
+
result.sparksql,
|
|
78
|
+
"utf8"
|
|
79
|
+
);
|
|
80
|
+
} else {
|
|
81
|
+
process.stdout.write("-- PySpark --\n");
|
|
82
|
+
process.stdout.write(result.pyspark + "\n");
|
|
83
|
+
process.stdout.write("-- SparkSQL --\n");
|
|
84
|
+
process.stdout.write(result.sparksql + "\n");
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function starPipelineSubcommand() {
|
|
89
|
+
return new Command("star-pipeline").description("Emit SCD-2 dim + FK-enrichment fact scaffolds from a designStar result.").requiredOption("--from-design <path>", "Path to a DesignStarResult JSON file.").option("--format <fmt>", "pyspark | sparksql | snowpark (default pyspark).", "pyspark").option("--schema <catalog.schema>", "Target catalog.schema prefix (default main).").option("--out <dir>", "Write pipeline files to this directory instead of stdout.").action(async (opts) => {
|
|
90
|
+
const raw = await fs.readFile(path.resolve(opts.fromDesign), "utf8");
|
|
91
|
+
const parsed = JSON.parse(raw);
|
|
92
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed) || !("factName" in parsed) || !Array.isArray(parsed.dimensions)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`--from-design must point to a DesignStarResult JSON (fields: factName, dimensions, ...).`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const design = {
|
|
98
|
+
measureColumns: [],
|
|
99
|
+
assumptions: [],
|
|
100
|
+
proposedInvocation: "",
|
|
101
|
+
needsReview: true,
|
|
102
|
+
rawModelText: "",
|
|
103
|
+
parseFailed: false,
|
|
104
|
+
...parsed
|
|
105
|
+
};
|
|
106
|
+
const fmt = opts.format ?? "pyspark";
|
|
107
|
+
const validFormats = ["pyspark", "sparksql", "snowpark"];
|
|
108
|
+
if (!validFormats.includes(fmt)) {
|
|
109
|
+
throw new Error(`--format must be pyspark | sparksql | snowpark (got '${opts.format}').`);
|
|
110
|
+
}
|
|
111
|
+
const result = generate.emitStarPipeline(design, {
|
|
112
|
+
format: fmt,
|
|
113
|
+
targetSchema: opts.schema
|
|
114
|
+
});
|
|
115
|
+
if (opts.out) {
|
|
116
|
+
const outDir = path.resolve(opts.out);
|
|
117
|
+
for (const [relPath, content] of result.files) {
|
|
118
|
+
const dest = path.join(outDir, relPath);
|
|
119
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
120
|
+
await fs.writeFile(dest, content, "utf8");
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
for (const [relPath, content] of result.files) {
|
|
124
|
+
process.stdout.write(`-- ${relPath} --
|
|
125
|
+
`);
|
|
126
|
+
process.stdout.write(content + "\n\n");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function generateCommand() {
|
|
132
|
+
const cmd = new Command("generate");
|
|
133
|
+
cmd.description("Code generation: ingest notebooks, SCD merge scripts, star-schema pipelines.");
|
|
134
|
+
cmd.addCommand(notebookSubcommand());
|
|
135
|
+
cmd.addCommand(scdSubcommand());
|
|
136
|
+
cmd.addCommand(starPipelineSubcommand());
|
|
137
|
+
return cmd;
|
|
138
|
+
}
|
|
139
|
+
async function readJsonRows(filePath) {
|
|
140
|
+
const raw = await fs.readFile(path.resolve(filePath), "utf8");
|
|
141
|
+
const trimmed = raw.trim();
|
|
142
|
+
if (trimmed.startsWith("[")) {
|
|
143
|
+
const parsed = JSON.parse(raw);
|
|
144
|
+
if (!Array.isArray(parsed)) throw new Error(`${filePath} must be a JSON array of objects.`);
|
|
145
|
+
return parsed;
|
|
146
|
+
}
|
|
147
|
+
return trimmed.split("\n").filter((l) => l.trim().length > 0).map((l, i) => {
|
|
148
|
+
const obj = JSON.parse(l);
|
|
149
|
+
if (obj === null || typeof obj !== "object" || Array.isArray(obj))
|
|
150
|
+
throw new Error(`Line ${i + 1} of ${filePath} is not a JSON object.`);
|
|
151
|
+
return obj;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function sanitizeFileStem(fqn) {
|
|
155
|
+
return fqn.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
generateCommand
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=generate-W7VLBDLI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/generate.ts"],"sourcesContent":["/**\n * `ddt generate` — code generation CLI (GENPY.5).\n *\n * Three subcommands wiring GENPY.2-4 substrates onto the operator surface:\n *\n * ddt generate notebook --from-json <path> --target <fqn> --source <path>\n * [--runtime <ver>] [--merge-schema] [--write-mode append|overwrite]\n * [--out <dir>]\n *\n * ddt generate scd --table <fqn> --natural-key <cols> [--type 1|2]\n * [--effective-from <col>] [--effective-to <col>]\n * [--is-current <col>] [--hash-cols <cols>]\n * [--surrogate-key <col>] [--out <dir>]\n *\n * ddt generate star-pipeline --from-design <path>\n * [--format pyspark|sparksql|snowpark]\n * [--schema <catalog.schema>] [--out <dir>]\n *\n * Mirrors `Snowflake/packages/cli/src/commands/generate.ts`.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { generate } from '@ddt-tools/core';\nimport type { SCDStrategy } from '@ddt-tools/core';\n\n// ── notebook ──────────────────────────────────────────────────────────────────\n\nfunction notebookSubcommand(): Command {\n return new Command('notebook')\n .description('Emit a Databricks PySpark ingest notebook from a JSON sample file.')\n .requiredOption('--from-json <path>', 'Path to a JSON data file (array of records or NDJSON).')\n .requiredOption('--target <fqn>', 'Target table FQN (catalog.schema.table).')\n .requiredOption('--source <path>', 'Source path for the read (S3 / DBFS / volume path).')\n .option('--runtime <ver>', 'Databricks Runtime version tag (e.g. dbr15.4-lts).', 'dbr14.3-lts')\n .option('--merge-schema', 'Emit mergeSchema option for schema evolution.', false)\n .option('--write-mode <mode>', 'append | overwrite (default append).', 'append')\n .option('--out <dir>', 'Write .ipynb + .py to this directory instead of stdout.')\n .action(async (opts) => {\n const rows = await readJsonRows(opts.fromJson);\n const schema = await generate.inferTypes(rows);\n const result = generate.emitIngestNotebook(schema, {\n targetFqn: opts.target,\n sourcePath: opts.source,\n runtime: opts.runtime,\n mergeSchema: Boolean(opts.mergeSchema),\n writeMode: opts.writeMode === 'overwrite' ? 'overwrite' : 'append',\n });\n\n if (opts.out) {\n await fs.mkdir(path.resolve(opts.out), { recursive: true });\n const stem = sanitizeFileStem(opts.target);\n await fs.writeFile(\n path.join(path.resolve(opts.out), `ingest_${stem}.ipynb`),\n JSON.stringify(result.ipynb, null, 2) + '\\n',\n 'utf8',\n );\n await fs.writeFile(\n path.join(path.resolve(opts.out), `ingest_${stem}.py`),\n result.py,\n 'utf8',\n );\n } else {\n process.stdout.write(result.py);\n }\n });\n}\n\n// ── scd ───────────────────────────────────────────────────────────────────────\n\nfunction scdSubcommand(): Command {\n return new Command('scd')\n .description('Emit SCD PySpark + SparkSQL merge code for a given table and strategy.')\n .requiredOption('--table <fqn>', 'Target table FQN (catalog.schema.table).')\n .requiredOption('--natural-key <cols>', 'Comma-separated natural key columns.')\n .option('--type <n>', 'SCD type (1 | 2, default 2).', '2')\n .option('--effective-from <col>', 'Effective-from timestamp column.', 'effective_from')\n .option('--effective-to <col>', 'Effective-to timestamp column.', 'effective_to')\n .option('--is-current <col>', 'Is-current boolean column (optional).')\n .option('--hash-cols <cols>', 'Comma-separated columns for hash-based change detection.')\n .option('--surrogate-key <col>', 'Surrogate key column (SCD-2 only).', 'id')\n .option(\n '--on-schema-change <action>',\n 'add-column | fail | rebuild (default add-column).',\n 'add-column',\n )\n .option('--out <dir>', 'Write .py + .sql files to this directory instead of stdout.')\n .action(async (opts) => {\n const naturalKey = String(opts.naturalKey)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n if (naturalKey.length === 0) {\n throw new Error('--natural-key must list at least one column name.');\n }\n\n const scdType = parseInt(opts.type, 10);\n if (scdType !== 1 && scdType !== 2) {\n throw new Error(`--type must be 1 or 2 (got '${opts.type}').`);\n }\n\n const hashCols = opts.hashCols\n ? String(opts.hashCols)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n\n const strategy: SCDStrategy = {\n type: scdType as 1 | 2,\n naturalKey,\n surrogateKeyColumn: scdType === 2 ? opts.surrogateKey : undefined,\n effectiveFromColumn: opts.effectiveFrom,\n effectiveToColumn: opts.effectiveTo,\n isCurrentColumn: opts.isCurrent,\n hashColumns: hashCols,\n onSchemaChange: opts.onSchemaChange as 'add-column' | 'fail' | 'rebuild',\n };\n\n const result = generate.emitScdPyspark(strategy, {\n targetFqn: opts.table,\n sourceFqn: `staging.stg_${sanitizeFileStem(opts.table)}`,\n projectionColumns: naturalKey,\n });\n\n if (opts.out) {\n const stem = sanitizeFileStem(opts.table);\n await fs.mkdir(path.resolve(opts.out), { recursive: true });\n await fs.writeFile(\n path.join(path.resolve(opts.out), `scd_${stem}.py`),\n result.pyspark,\n 'utf8',\n );\n await fs.writeFile(\n path.join(path.resolve(opts.out), `scd_${stem}.sql`),\n result.sparksql,\n 'utf8',\n );\n } else {\n process.stdout.write('-- PySpark --\\n');\n process.stdout.write(result.pyspark + '\\n');\n process.stdout.write('-- SparkSQL --\\n');\n process.stdout.write(result.sparksql + '\\n');\n }\n });\n}\n\n// ── star-pipeline ─────────────────────────────────────────────────────────────\n\nfunction starPipelineSubcommand(): Command {\n return new Command('star-pipeline')\n .description('Emit SCD-2 dim + FK-enrichment fact scaffolds from a designStar result.')\n .requiredOption('--from-design <path>', 'Path to a DesignStarResult JSON file.')\n .option('--format <fmt>', 'pyspark | sparksql | snowpark (default pyspark).', 'pyspark')\n .option('--schema <catalog.schema>', 'Target catalog.schema prefix (default main).')\n .option('--out <dir>', 'Write pipeline files to this directory instead of stdout.')\n .action(async (opts) => {\n const raw = await fs.readFile(path.resolve(opts.fromDesign), 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed) ||\n !('factName' in parsed) ||\n !Array.isArray((parsed as Record<string, unknown>).dimensions)\n ) {\n throw new Error(\n `--from-design must point to a DesignStarResult JSON (fields: factName, dimensions, ...).`,\n );\n }\n const design = {\n measureColumns: [],\n assumptions: [],\n proposedInvocation: '',\n needsReview: true as const,\n rawModelText: '',\n parseFailed: false,\n ...(parsed as Record<string, unknown>),\n } as unknown as Parameters<typeof generate.emitStarPipeline>[0];\n\n const fmt = (opts.format ?? 'pyspark') as generate.StarPipelineFormat;\n const validFormats: generate.StarPipelineFormat[] = ['pyspark', 'sparksql', 'snowpark'];\n if (!validFormats.includes(fmt)) {\n throw new Error(`--format must be pyspark | sparksql | snowpark (got '${opts.format}').`);\n }\n\n const result = generate.emitStarPipeline(design, {\n format: fmt,\n targetSchema: opts.schema,\n });\n\n if (opts.out) {\n const outDir = path.resolve(opts.out);\n for (const [relPath, content] of result.files) {\n const dest = path.join(outDir, relPath);\n await fs.mkdir(path.dirname(dest), { recursive: true });\n await fs.writeFile(dest, content, 'utf8');\n }\n } else {\n for (const [relPath, content] of result.files) {\n process.stdout.write(`-- ${relPath} --\\n`);\n process.stdout.write(content + '\\n\\n');\n }\n }\n });\n}\n\n// ── public factory ────────────────────────────────────────────────────────────\n\nexport function generateCommand(): Command {\n const cmd = new Command('generate');\n cmd.description('Code generation: ingest notebooks, SCD merge scripts, star-schema pipelines.');\n cmd.addCommand(notebookSubcommand());\n cmd.addCommand(scdSubcommand());\n cmd.addCommand(starPipelineSubcommand());\n return cmd;\n}\n\n// ── helpers ───────────────────────────────────────────────────────────────────\n\nasync function readJsonRows(filePath: string): Promise<Record<string, unknown>[]> {\n const raw = await fs.readFile(path.resolve(filePath), 'utf8');\n const trimmed = raw.trim();\n if (trimmed.startsWith('[')) {\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) throw new Error(`${filePath} must be a JSON array of objects.`);\n return parsed as Record<string, unknown>[];\n }\n // NDJSON\n return trimmed\n .split('\\n')\n .filter((l) => l.trim().length > 0)\n .map((l, i) => {\n const obj: unknown = JSON.parse(l);\n if (obj === null || typeof obj !== 'object' || Array.isArray(obj))\n throw new Error(`Line ${i + 1} of ${filePath} is not a JSON object.`);\n return obj as Record<string, unknown>;\n });\n}\n\n/** `main.marketing.fact_sales` → `main_marketing_fact_sales` */\nfunction sanitizeFileStem(fqn: string): string {\n return fqn\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_|_$/g, '');\n}\n"],"mappings":"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","names":[]}
|