|
1 | | -import type { Model } from '@zenstackhq/language/ast'; |
| 1 | +import { Model, Enum, DataModel } from '@zenstackhq/language/ast'; |
2 | 2 | import { ZModelCodeGenerator } from '@zenstackhq/sdk'; |
3 | 3 | import fs from 'node:fs'; |
4 | 4 | import path from 'node:path'; |
5 | 5 | import { execPrisma } from '../utils/exec-utils'; |
6 | 6 | import { generateTempPrismaSchema, getSchemaFile, handleSubProcessError, requireDataSourceUrl, loadSchemaDocumentWithServices } from './action-utils'; |
7 | 7 | import { syncEnums, syncRelation, syncTable, type Relation } from './pull'; |
8 | 8 | import { providers } from './pull/provider'; |
9 | | -import { getDatasource } from './pull/utils'; |
| 9 | +import { getDatasource, getDbName } from './pull/utils'; |
10 | 10 | import { config } from '@dotenvx/dotenvx'; |
11 | 11 |
|
12 | 12 | type PushOptions = { |
@@ -115,18 +115,93 @@ async function runPull(options: PullOptions) { |
115 | 115 | syncRelation({ model: newModel, relation, services, options }); |
116 | 116 | } |
117 | 117 |
|
118 | | - //TODO: diff models and apply changes only |
| 118 | + const cwd = new URL(`file://${process.cwd()}`).pathname; |
| 119 | + const docs = services.shared.workspace.LangiumDocuments.all |
| 120 | + .filter(({ uri }) => uri.path.toLowerCase().startsWith(cwd.toLowerCase())) |
| 121 | + .toArray(); |
| 122 | + const docsSet = new Set(docs.map((d) => d.uri.toString())); |
| 123 | + console.log(docsSet); |
| 124 | + newModel.declarations |
| 125 | + .filter((d) => [DataModel, Enum].includes(d.$type)) |
| 126 | + .forEach((_declaration) => { |
| 127 | + const declaration = _declaration as DataModel | Enum; |
| 128 | + const declarations = services.shared.workspace.IndexManager.allElements(declaration.$type, docsSet); |
| 129 | + const originalModel = declarations.find((d) => getDbName(d.node as any) === getDbName(declaration)) |
| 130 | + ?.node as DataModel | Enum | undefined; |
| 131 | + if (!originalModel) { |
| 132 | + model.declarations.push(declaration); |
| 133 | + (declaration as any).$container = model; |
| 134 | + return; |
| 135 | + } |
| 136 | + |
| 137 | + declaration.fields.forEach((f) => { |
| 138 | + const originalField = originalModel.fields.find((d) => getDbName(d) === getDbName(f)); |
| 139 | + |
| 140 | + if (!originalField) { |
| 141 | + console.log(`Added field ${f.name} to ${originalModel.name}`); |
| 142 | + (f as any).$container = originalModel; |
| 143 | + originalModel.fields.push(f as any); |
| 144 | + return; |
| 145 | + } |
| 146 | + //TODO: update field |
| 147 | + }); |
| 148 | + originalModel.fields |
| 149 | + .filter((f) => !declaration.fields.find((d) => getDbName(d) === getDbName(f))) |
| 150 | + .forEach((f) => { |
| 151 | + const model = f.$container; |
| 152 | + const index = model.fields.findIndex((d) => d === f); |
| 153 | + model.fields.splice(index, 1); |
| 154 | + console.log(`Delete field ${f.name}`); |
| 155 | + }); |
| 156 | + }); |
| 157 | + |
| 158 | + services.shared.workspace.IndexManager.allElements('DataModel', docsSet) |
| 159 | + .filter( |
| 160 | + (declaration) => |
| 161 | + !newModel.declarations.find((d) => getDbName(d) === getDbName(declaration.node as any)), |
| 162 | + ) |
| 163 | + .forEach((decl) => { |
| 164 | + const model = decl.node!.$container as Model; |
| 165 | + const index = model.declarations.findIndex((d) => d === decl.node); |
| 166 | + model.declarations.splice(index, 1); |
| 167 | + console.log(`Delete model ${decl.name}`); |
| 168 | + }); |
| 169 | + services.shared.workspace.IndexManager.allElements('Enum', docsSet) |
| 170 | + .filter( |
| 171 | + (declaration) => |
| 172 | + !newModel.declarations.find((d) => getDbName(d) === getDbName(declaration.node as any)), |
| 173 | + ) |
| 174 | + .forEach((decl) => { |
| 175 | + const model = decl.node!.$container as Model; |
| 176 | + const index = model.declarations.findIndex((d) => d === decl.node); |
| 177 | + model.declarations.splice(index, 1); |
| 178 | + console.log(`Delete enum ${decl.name}`); |
| 179 | + }); |
| 180 | + |
| 181 | + if (options.out && !fs.lstatSync(options.out).isFile()) { |
| 182 | + throw new Error(`Output path ${options.out} is not a file`); |
| 183 | + } |
119 | 184 |
|
120 | | - const generator = new ZModelCodeGenerator(); |
| 185 | + const generator = new ZModelCodeGenerator({ |
| 186 | + //TODO: make configurable |
| 187 | + quote: 'double', |
| 188 | + }); |
121 | 189 |
|
122 | | - const zmodelSchema = generator.generate(newModel); |
| 190 | + if (options.out) { |
| 191 | + const zmodelSchema = generator.generate(newModel); |
123 | 192 |
|
124 | | - console.log(options.out ? `Writing to ${options.out}` : schemaFile); |
| 193 | + console.log(`Writing to ${options.out}`); |
125 | 194 |
|
126 | | - const outPath = options.out ? path.resolve(options.out) : schemaFile; |
127 | | - console.log(outPath); |
| 195 | + const outPath = options.out ? path.resolve(options.out) : schemaFile; |
128 | 196 |
|
129 | | - fs.writeFileSync(outPath, zmodelSchema); |
| 197 | + fs.writeFileSync(outPath, zmodelSchema); |
| 198 | + } else { |
| 199 | + docs.forEach(({ uri, parseResult: { value: model } }) => { |
| 200 | + const zmodelSchema = generator.generate(model); |
| 201 | + console.log(`Writing to ${uri.path}`); |
| 202 | + fs.writeFileSync(uri.fsPath, zmodelSchema); |
| 203 | + }); |
| 204 | + } |
130 | 205 | } catch (error) { |
131 | 206 | console.log(error); |
132 | 207 | throw error; |
|
0 commit comments