@prisma-next/psl-printer 0.5.0-dev.66 → 0.5.0-dev.68

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/print-psl.ts"],"sourcesContent":[],"mappings":";;;iBAIgB,eAAA,MAAqB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/print-psl.ts"],"mappings":";;;iBAIgB,eAAA,CAAgB,GAAA,EAAK,cAAA"}
package/dist/index.mjs CHANGED
@@ -105,7 +105,6 @@ function createNormalizedEnumMemberBaseName(value) {
105
105
  if (isNormalizedEnumMemberReservedWord(normalized) || /^\d/.test(normalized)) normalized = `_${normalized}`;
106
106
  return normalized;
107
107
  }
108
-
109
108
  //#endregion
110
109
  //#region src/ast-to-print-document.ts
111
110
  const DEFAULT_AST_PRINT_HEADER = "// Contract inferred from the live database schema. Edit as needed, then run `prisma-next contract emit`.";
@@ -289,13 +288,12 @@ function topologicalSortModels(models, deps) {
289
288
  for (const name of sortedNames) visit(name);
290
289
  return result;
291
290
  }
292
-
293
291
  //#endregion
294
292
  //#region src/print-psl.ts
295
293
  function printPslFromAst(ast) {
296
294
  return serializePrintDocument(astDocumentToPrintDocument(ast));
297
295
  }
298
-
299
296
  //#endregion
300
297
  export { printPslFromAst as printPsl };
298
+
301
299
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["sections: string[]","lines: string[]","namedTypes: PrinterNamedType[]","enums: PrinterEnum[]","mapName: string | undefined","modelAttrStrings: string[]","attrStrings: string[]","result: PslModel[]"],"sources":["../src/serialize-print-document.ts","../src/ast-to-print-document.ts","../src/print-psl.ts"],"sourcesContent":["import type { PrintDocument } from './print-document';\nimport type { PrinterEnumValue, PrinterField, PrinterNamedType } from './types';\n\nconst PSL_IDENTIFIER_PATTERN = /^[A-Za-z_]\\w*$/;\nconst ENUM_MEMBER_RESERVED_WORDS = new Set([\n 'datasource',\n 'default',\n 'enum',\n 'generator',\n 'model',\n 'type',\n 'types',\n]);\n\nexport function escapePslString(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r');\n}\n\nexport function serializePrintDocument(doc: PrintDocument): string {\n const sections: string[] = [];\n\n sections.push(doc.headerComment);\n\n const namedTypeEntries = [...doc.namedTypes].sort((a, b) => a.name.localeCompare(b.name));\n if (namedTypeEntries.length > 0) {\n sections.push(serializeTypesBlock(namedTypeEntries));\n }\n\n const enumsSorted = [...doc.enums].sort((a, b) => a.name.localeCompare(b.name));\n for (const e of enumsSorted) {\n sections.push(serializeEnum(e));\n }\n\n for (const model of doc.models) {\n sections.push(serializeModel(model));\n }\n\n return `${sections.join('\\n\\n')}\\n`;\n}\n\nfunction serializeTypesBlock(namedTypes: readonly PrinterNamedType[]): string {\n const lines = ['types {'];\n for (const nt of namedTypes) {\n const attrStr = nt.attributes.length > 0 ? ` ${nt.attributes.join(' ')}` : '';\n lines.push(` ${nt.name} = ${nt.baseType}${attrStr}`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction serializeEnum(e: {\n name: string;\n mapName?: string | undefined;\n values: readonly PrinterEnumValue[];\n}): string {\n const lines = [`enum ${e.name} {`];\n const usedNames = new Set<string>();\n for (const value of e.values) {\n const memberName = normalizeEnumMemberName(value.name, usedNames);\n // Emit a per-member `@map(\"...\")` whenever the printed identifier differs\n // from the original storage label (e.g. PostgreSQL enum labels with\n // hyphens that get normalised to camelCase, reserved words that get\n // `_`-prefixed, or names that collide and get a numeric suffix), or when\n // the AST carried an explicit `mapName` from a parsed source. Without\n // this, parsing the emitted PSL would lose the original storage label and\n // a subsequent `contract emit` would talk to the wrong DB enum value.\n const explicitMap = value.mapName;\n const storageLabel =\n explicitMap !== undefined ? explicitMap : memberName !== value.name ? value.name : undefined;\n if (storageLabel !== undefined) {\n lines.push(` ${memberName} @map(\"${escapePslString(storageLabel)}\")`);\n } else {\n lines.push(` ${memberName}`);\n }\n usedNames.add(memberName);\n }\n if (e.mapName) {\n lines.push('');\n lines.push(` @@map(\"${escapePslString(e.mapName)}\")`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction serializeModel(model: import('./types').PrinterModel): string {\n const lines: string[] = [];\n\n if (model.comment) {\n lines.push(model.comment);\n }\n lines.push(`model ${model.name} {`);\n\n const idFields = model.fields.filter((f) => f.isId);\n const scalarFields = model.fields.filter((f) => !f.isId && !f.isRelation);\n const relationFields = model.fields.filter((f) => f.isRelation);\n\n const allOrderedFields = [...idFields, ...scalarFields, ...relationFields];\n\n if (allOrderedFields.length > 0) {\n const maxNameLen = Math.max(...allOrderedFields.map((f) => f.name.length));\n const maxTypeLen = Math.max(...allOrderedFields.map((f) => formatFieldType(f).length));\n\n for (const field of allOrderedFields) {\n const typePart = formatFieldType(field);\n const paddedName = field.name.padEnd(maxNameLen);\n const paddedType = typePart.padEnd(maxTypeLen);\n\n if (field.comment) {\n lines.push(` ${field.comment}`);\n }\n\n const attrStr = field.attributes.length > 0 ? ` ${field.attributes.join(' ')}` : '';\n lines.push(` ${paddedName} ${paddedType}${attrStr}`.trimEnd());\n }\n }\n\n if (model.modelAttributes.length > 0) {\n if (allOrderedFields.length > 0) {\n lines.push('');\n }\n for (const attr of model.modelAttributes) {\n lines.push(` ${attr}`);\n }\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction formatFieldType(field: PrinterField): string {\n let type = field.typeName;\n if (field.list) {\n type += '[]';\n } else if (field.optional) {\n type += '?';\n }\n return type;\n}\n\nfunction createUniqueFieldName(desiredName: string, usedFieldNames: ReadonlySet<string>): string {\n if (!usedFieldNames.has(desiredName)) {\n return desiredName;\n }\n\n let counter = 2;\n while (usedFieldNames.has(`${desiredName}${counter}`)) {\n counter++;\n }\n return `${desiredName}${counter}`;\n}\n\nfunction isNormalizedEnumMemberReservedWord(value: string): boolean {\n return ENUM_MEMBER_RESERVED_WORDS.has(value.toLowerCase());\n}\n\nfunction normalizeEnumMemberName(value: string, usedNames: ReadonlySet<string>): string {\n const desiredName =\n PSL_IDENTIFIER_PATTERN.test(value) && !isNormalizedEnumMemberReservedWord(value)\n ? value\n : createNormalizedEnumMemberBaseName(value);\n\n return createUniqueFieldName(desiredName, usedNames);\n}\n\nfunction createNormalizedEnumMemberBaseName(value: string): string {\n const tokens = value.match(/[A-Za-z0-9]+/g)?.map((token) => token.toLowerCase()) ?? [];\n let normalized = tokens[0] ?? 'value';\n\n for (const token of tokens.slice(1)) {\n normalized += token.charAt(0).toUpperCase() + token.slice(1);\n }\n\n if (isNormalizedEnumMemberReservedWord(normalized) || /^\\d/.test(normalized)) {\n normalized = `_${normalized}`;\n }\n\n return normalized;\n}\n","import type {\n PslAttribute,\n PslAttributeArgument,\n PslDocumentAst,\n PslEnum,\n PslField,\n PslModel,\n PslNamedTypeDeclaration,\n PslTypeConstructorCall,\n} from '@prisma-next/framework-components/psl-ast';\nimport type { PrintDocument } from './print-document';\nimport { escapePslString } from './serialize-print-document';\nimport type { PrinterEnum, PrinterField, PrinterModel, PrinterNamedType } from './types';\n\n// `contract infer` produces a starting-point PSL contract from a live database\n// schema; the user is expected to edit it (rename models/fields, tighten types,\n// add `@id` where introspection couldn't infer one, etc.) and then run\n// `contract emit` to produce the canonical artifacts. The header invites that\n// workflow rather than warning against it.\nconst DEFAULT_AST_PRINT_HEADER =\n '// Contract inferred from the live database schema. Edit as needed, then run `prisma-next contract emit`.';\n\nexport function astDocumentToPrintDocument(ast: PslDocumentAst): PrintDocument {\n const modelNames = new Set(ast.models.map((m) => m.name));\n const deps = buildModelFkDeps(ast.models, modelNames);\n const sortedModels = topologicalSortModels(ast.models, deps);\n\n const namedTypes: PrinterNamedType[] = ast.types\n ? ast.types.declarations.map(namedTypeDeclarationToPrinterNamedType)\n : [];\n\n const enums: PrinterEnum[] = ast.enums.map(enumToPrinterEnum);\n\n const printerModels = sortedModels.map((m) => modelToPrinterModel(m));\n\n return {\n headerComment: DEFAULT_AST_PRINT_HEADER,\n namedTypes,\n enums: enums.map((e) => ({\n name: e.name,\n mapName: e.mapName,\n values: e.values,\n })),\n models: printerModels,\n };\n}\n\nexport function renderPslAttribute(attr: PslAttribute): string {\n const prefix = attr.target === 'model' || attr.target === 'enum' ? '@@' : '@';\n if (attr.args.length === 0) {\n return `${prefix}${attr.name}`;\n }\n const inner = attr.args.map(renderAttributeArgument).join(', ');\n return `${prefix}${attr.name}(${inner})`;\n}\n\nfunction renderAttributeArgument(arg: PslAttributeArgument): string {\n if (arg.kind === 'positional') {\n return arg.value;\n }\n return `${arg.name}: ${arg.value}`;\n}\n\nfunction namedTypeDeclarationToPrinterNamedType(decl: PslNamedTypeDeclaration): PrinterNamedType {\n const base =\n decl.baseType ??\n (decl.typeConstructor !== undefined ? formatTypeConstructor(decl.typeConstructor) : '');\n const attributes = decl.attributes.map(renderPslAttribute);\n return {\n name: decl.name,\n baseType: base,\n attributes,\n };\n}\n\nfunction formatTypeConstructor(tc: PslTypeConstructorCall): string {\n const path = tc.path.join('.');\n if (tc.args.length === 0) {\n return path;\n }\n return `${path}(${tc.args.map(renderAttributeArgument).join(', ')})`;\n}\n\nfunction enumToPrinterEnum(en: PslEnum): PrinterEnum {\n let mapName: string | undefined;\n for (const a of en.attributes) {\n if (a.name === 'map' && a.target === 'enum') {\n const quoted = getPositionalStringArg(a, 0);\n if (quoted !== undefined) {\n mapName = quoted;\n }\n }\n }\n return {\n name: en.name,\n mapName,\n values: en.values.map((v) => ({\n name: v.name,\n ...(v.mapName !== undefined ? { mapName: v.mapName } : {}),\n })),\n };\n}\n\nfunction getPositionalStringArg(attr: PslAttribute, index: number): string | undefined {\n const positional = attr.args.filter((a) => a.kind === 'positional');\n const raw = positional[index]?.value.trim();\n if (!raw) return undefined;\n const m = raw.match(/^(['\"])(.*)\\1$/);\n if (!m) return undefined;\n return unescapePslString(m[2] as string);\n}\n\n/**\n * Inverse of `escapePslString`. The parser stores quoted-literal arguments with\n * their PSL escape sequences (`\\\\`, `\\\"`, `\\n`, `\\r`) intact; when we round-trip\n * a value through `getPositionalStringArg` and re-render via `escapePslString`,\n * we must decode it once on extraction to avoid double-escaping the same\n * sequences on output.\n */\nfunction unescapePslString(value: string): string {\n let result = '';\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n if (ch !== 0x5c /* '\\\\' */ || i + 1 >= value.length) {\n result += value[i];\n continue;\n }\n const next = value[i + 1];\n if (next === '\\\\' || next === '\"' || next === \"'\") {\n result += next;\n } else if (next === 'n') {\n result += '\\n';\n } else if (next === 'r') {\n result += '\\r';\n } else {\n result += '\\\\';\n result += next;\n }\n i++;\n }\n return result;\n}\n\nfunction modelToPrinterModel(model: PslModel): PrinterModel {\n let mapName: string | undefined;\n const modelAttrStrings: string[] = [];\n\n for (const a of model.attributes) {\n if (a.name === 'map' && a.target === 'model') {\n mapName = getPositionalStringArg(a, 0) ?? mapName;\n continue;\n }\n modelAttrStrings.push(renderPslAttribute(a));\n }\n\n if (mapName !== undefined) {\n modelAttrStrings.push(`@@map(\"${escapePslString(mapName)}\")`);\n }\n\n const printerFields = model.fields.map((f) => fieldToPrinterField(f));\n\n return {\n name: model.name,\n mapName,\n fields: printerFields,\n modelAttributes: modelAttrStrings,\n comment: model.comment,\n };\n}\n\nfunction fieldToPrinterField(field: PslField): PrinterField {\n const typeName =\n field.typeConstructor !== undefined\n ? formatTypeConstructor(field.typeConstructor)\n : field.typeName;\n\n let mapName: string | undefined;\n const attrStrings: string[] = [];\n\n for (const a of field.attributes) {\n if (a.name === 'map' && a.target === 'field') {\n mapName = getPositionalStringArg(a, 0) ?? mapName;\n continue;\n }\n attrStrings.push(renderPslAttribute(a));\n }\n\n if (mapName !== undefined) {\n attrStrings.push(`@map(\"${escapePslString(mapName)}\")`);\n }\n\n const isRelation = field.attributes.some((a) => a.name === 'relation' && a.target === 'field');\n\n const isUnsupported = typeName.startsWith('Unsupported(');\n\n const isId = field.attributes.some((a) => a.name === 'id' && a.target === 'field');\n\n return {\n name: field.name,\n typeName,\n optional: field.optional,\n list: field.list,\n attributes: attrStrings,\n mapName,\n isId,\n isRelation,\n isUnsupported,\n comment: undefined,\n };\n}\n\nfunction buildModelFkDeps(\n models: readonly PslModel[],\n modelNames: ReadonlySet<string>,\n): Map<string, Set<string>> {\n const deps = new Map<string, Set<string>>();\n for (const m of models) {\n deps.set(m.name, new Set());\n }\n\n for (const m of models) {\n for (const field of m.fields) {\n const refModel = relationReferencedModel(field, modelNames);\n if (!refModel || refModel === m.name) continue;\n if (!hasFullRelation(field)) continue;\n (deps.get(m.name) as Set<string>).add(refModel);\n }\n }\n\n return deps;\n}\n\nfunction hasFullRelation(field: PslField): boolean {\n const rel = field.attributes.find((a) => a.name === 'relation' && a.target === 'field');\n if (!rel) return false;\n const named = Object.fromEntries(\n rel.args\n .filter(\n (a): a is import('@prisma-next/framework-components/psl-ast').PslAttributeNamedArgument =>\n a.kind === 'named',\n )\n .map((a) => [a.name, a.value.trim()]),\n );\n return named['fields'] !== undefined && named['references'] !== undefined;\n}\n\nfunction relationReferencedModel(\n field: PslField,\n modelNames: ReadonlySet<string>,\n): string | undefined {\n const head = field.typeConstructor?.path[0];\n const raw = head ?? field.typeName.replace(/\\?$/, '').replace(/\\[\\]$/, '');\n if (raw.length === 0) {\n return undefined;\n }\n return modelNames.has(raw) ? raw : undefined;\n}\n\nfunction topologicalSortModels(\n models: readonly PslModel[],\n deps: ReadonlyMap<string, Set<string>>,\n): PslModel[] {\n const byName = new Map(models.map((m) => [m.name, m]));\n const result: PslModel[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const sortedNames = [...deps.keys()].sort();\n\n function visit(name: string): void {\n if (visited.has(name)) return;\n if (visiting.has(name)) return;\n visiting.add(name);\n\n const sortedDeps = [...(deps.get(name) ?? new Set())].sort();\n for (const dep of sortedDeps) {\n visit(dep);\n }\n\n visiting.delete(name);\n visited.add(name);\n const model = byName.get(name);\n if (model) {\n result.push(model);\n }\n }\n\n for (const name of sortedNames) {\n visit(name);\n }\n\n return result;\n}\n","import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';\nimport { astDocumentToPrintDocument } from './ast-to-print-document';\nimport { serializePrintDocument } from './serialize-print-document';\n\nexport function printPslFromAst(ast: PslDocumentAst): string {\n const doc = astDocumentToPrintDocument(ast);\n return serializePrintDocument(doc);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/serialize-print-document.ts","../src/ast-to-print-document.ts","../src/print-psl.ts"],"sourcesContent":["import type { PrintDocument } from './print-document';\nimport type { PrinterEnumValue, PrinterField, PrinterNamedType } from './types';\n\nconst PSL_IDENTIFIER_PATTERN = /^[A-Za-z_]\\w*$/;\nconst ENUM_MEMBER_RESERVED_WORDS = new Set([\n 'datasource',\n 'default',\n 'enum',\n 'generator',\n 'model',\n 'type',\n 'types',\n]);\n\nexport function escapePslString(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r');\n}\n\nexport function serializePrintDocument(doc: PrintDocument): string {\n const sections: string[] = [];\n\n sections.push(doc.headerComment);\n\n const namedTypeEntries = [...doc.namedTypes].sort((a, b) => a.name.localeCompare(b.name));\n if (namedTypeEntries.length > 0) {\n sections.push(serializeTypesBlock(namedTypeEntries));\n }\n\n const enumsSorted = [...doc.enums].sort((a, b) => a.name.localeCompare(b.name));\n for (const e of enumsSorted) {\n sections.push(serializeEnum(e));\n }\n\n for (const model of doc.models) {\n sections.push(serializeModel(model));\n }\n\n return `${sections.join('\\n\\n')}\\n`;\n}\n\nfunction serializeTypesBlock(namedTypes: readonly PrinterNamedType[]): string {\n const lines = ['types {'];\n for (const nt of namedTypes) {\n const attrStr = nt.attributes.length > 0 ? ` ${nt.attributes.join(' ')}` : '';\n lines.push(` ${nt.name} = ${nt.baseType}${attrStr}`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction serializeEnum(e: {\n name: string;\n mapName?: string | undefined;\n values: readonly PrinterEnumValue[];\n}): string {\n const lines = [`enum ${e.name} {`];\n const usedNames = new Set<string>();\n for (const value of e.values) {\n const memberName = normalizeEnumMemberName(value.name, usedNames);\n // Emit a per-member `@map(\"...\")` whenever the printed identifier differs\n // from the original storage label (e.g. PostgreSQL enum labels with\n // hyphens that get normalised to camelCase, reserved words that get\n // `_`-prefixed, or names that collide and get a numeric suffix), or when\n // the AST carried an explicit `mapName` from a parsed source. Without\n // this, parsing the emitted PSL would lose the original storage label and\n // a subsequent `contract emit` would talk to the wrong DB enum value.\n const explicitMap = value.mapName;\n const storageLabel =\n explicitMap !== undefined ? explicitMap : memberName !== value.name ? value.name : undefined;\n if (storageLabel !== undefined) {\n lines.push(` ${memberName} @map(\"${escapePslString(storageLabel)}\")`);\n } else {\n lines.push(` ${memberName}`);\n }\n usedNames.add(memberName);\n }\n if (e.mapName) {\n lines.push('');\n lines.push(` @@map(\"${escapePslString(e.mapName)}\")`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction serializeModel(model: import('./types').PrinterModel): string {\n const lines: string[] = [];\n\n if (model.comment) {\n lines.push(model.comment);\n }\n lines.push(`model ${model.name} {`);\n\n const idFields = model.fields.filter((f) => f.isId);\n const scalarFields = model.fields.filter((f) => !f.isId && !f.isRelation);\n const relationFields = model.fields.filter((f) => f.isRelation);\n\n const allOrderedFields = [...idFields, ...scalarFields, ...relationFields];\n\n if (allOrderedFields.length > 0) {\n const maxNameLen = Math.max(...allOrderedFields.map((f) => f.name.length));\n const maxTypeLen = Math.max(...allOrderedFields.map((f) => formatFieldType(f).length));\n\n for (const field of allOrderedFields) {\n const typePart = formatFieldType(field);\n const paddedName = field.name.padEnd(maxNameLen);\n const paddedType = typePart.padEnd(maxTypeLen);\n\n if (field.comment) {\n lines.push(` ${field.comment}`);\n }\n\n const attrStr = field.attributes.length > 0 ? ` ${field.attributes.join(' ')}` : '';\n lines.push(` ${paddedName} ${paddedType}${attrStr}`.trimEnd());\n }\n }\n\n if (model.modelAttributes.length > 0) {\n if (allOrderedFields.length > 0) {\n lines.push('');\n }\n for (const attr of model.modelAttributes) {\n lines.push(` ${attr}`);\n }\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction formatFieldType(field: PrinterField): string {\n let type = field.typeName;\n if (field.list) {\n type += '[]';\n } else if (field.optional) {\n type += '?';\n }\n return type;\n}\n\nfunction createUniqueFieldName(desiredName: string, usedFieldNames: ReadonlySet<string>): string {\n if (!usedFieldNames.has(desiredName)) {\n return desiredName;\n }\n\n let counter = 2;\n while (usedFieldNames.has(`${desiredName}${counter}`)) {\n counter++;\n }\n return `${desiredName}${counter}`;\n}\n\nfunction isNormalizedEnumMemberReservedWord(value: string): boolean {\n return ENUM_MEMBER_RESERVED_WORDS.has(value.toLowerCase());\n}\n\nfunction normalizeEnumMemberName(value: string, usedNames: ReadonlySet<string>): string {\n const desiredName =\n PSL_IDENTIFIER_PATTERN.test(value) && !isNormalizedEnumMemberReservedWord(value)\n ? value\n : createNormalizedEnumMemberBaseName(value);\n\n return createUniqueFieldName(desiredName, usedNames);\n}\n\nfunction createNormalizedEnumMemberBaseName(value: string): string {\n const tokens = value.match(/[A-Za-z0-9]+/g)?.map((token) => token.toLowerCase()) ?? [];\n let normalized = tokens[0] ?? 'value';\n\n for (const token of tokens.slice(1)) {\n normalized += token.charAt(0).toUpperCase() + token.slice(1);\n }\n\n if (isNormalizedEnumMemberReservedWord(normalized) || /^\\d/.test(normalized)) {\n normalized = `_${normalized}`;\n }\n\n return normalized;\n}\n","import type {\n PslAttribute,\n PslAttributeArgument,\n PslDocumentAst,\n PslEnum,\n PslField,\n PslModel,\n PslNamedTypeDeclaration,\n PslTypeConstructorCall,\n} from '@prisma-next/framework-components/psl-ast';\nimport type { PrintDocument } from './print-document';\nimport { escapePslString } from './serialize-print-document';\nimport type { PrinterEnum, PrinterField, PrinterModel, PrinterNamedType } from './types';\n\n// `contract infer` produces a starting-point PSL contract from a live database\n// schema; the user is expected to edit it (rename models/fields, tighten types,\n// add `@id` where introspection couldn't infer one, etc.) and then run\n// `contract emit` to produce the canonical artifacts. The header invites that\n// workflow rather than warning against it.\nconst DEFAULT_AST_PRINT_HEADER =\n '// Contract inferred from the live database schema. Edit as needed, then run `prisma-next contract emit`.';\n\nexport function astDocumentToPrintDocument(ast: PslDocumentAst): PrintDocument {\n const modelNames = new Set(ast.models.map((m) => m.name));\n const deps = buildModelFkDeps(ast.models, modelNames);\n const sortedModels = topologicalSortModels(ast.models, deps);\n\n const namedTypes: PrinterNamedType[] = ast.types\n ? ast.types.declarations.map(namedTypeDeclarationToPrinterNamedType)\n : [];\n\n const enums: PrinterEnum[] = ast.enums.map(enumToPrinterEnum);\n\n const printerModels = sortedModels.map((m) => modelToPrinterModel(m));\n\n return {\n headerComment: DEFAULT_AST_PRINT_HEADER,\n namedTypes,\n enums: enums.map((e) => ({\n name: e.name,\n mapName: e.mapName,\n values: e.values,\n })),\n models: printerModels,\n };\n}\n\nexport function renderPslAttribute(attr: PslAttribute): string {\n const prefix = attr.target === 'model' || attr.target === 'enum' ? '@@' : '@';\n if (attr.args.length === 0) {\n return `${prefix}${attr.name}`;\n }\n const inner = attr.args.map(renderAttributeArgument).join(', ');\n return `${prefix}${attr.name}(${inner})`;\n}\n\nfunction renderAttributeArgument(arg: PslAttributeArgument): string {\n if (arg.kind === 'positional') {\n return arg.value;\n }\n return `${arg.name}: ${arg.value}`;\n}\n\nfunction namedTypeDeclarationToPrinterNamedType(decl: PslNamedTypeDeclaration): PrinterNamedType {\n const base =\n decl.baseType ??\n (decl.typeConstructor !== undefined ? formatTypeConstructor(decl.typeConstructor) : '');\n const attributes = decl.attributes.map(renderPslAttribute);\n return {\n name: decl.name,\n baseType: base,\n attributes,\n };\n}\n\nfunction formatTypeConstructor(tc: PslTypeConstructorCall): string {\n const path = tc.path.join('.');\n if (tc.args.length === 0) {\n return path;\n }\n return `${path}(${tc.args.map(renderAttributeArgument).join(', ')})`;\n}\n\nfunction enumToPrinterEnum(en: PslEnum): PrinterEnum {\n let mapName: string | undefined;\n for (const a of en.attributes) {\n if (a.name === 'map' && a.target === 'enum') {\n const quoted = getPositionalStringArg(a, 0);\n if (quoted !== undefined) {\n mapName = quoted;\n }\n }\n }\n return {\n name: en.name,\n mapName,\n values: en.values.map((v) => ({\n name: v.name,\n ...(v.mapName !== undefined ? { mapName: v.mapName } : {}),\n })),\n };\n}\n\nfunction getPositionalStringArg(attr: PslAttribute, index: number): string | undefined {\n const positional = attr.args.filter((a) => a.kind === 'positional');\n const raw = positional[index]?.value.trim();\n if (!raw) return undefined;\n const m = raw.match(/^(['\"])(.*)\\1$/);\n if (!m) return undefined;\n return unescapePslString(m[2] as string);\n}\n\n/**\n * Inverse of `escapePslString`. The parser stores quoted-literal arguments with\n * their PSL escape sequences (`\\\\`, `\\\"`, `\\n`, `\\r`) intact; when we round-trip\n * a value through `getPositionalStringArg` and re-render via `escapePslString`,\n * we must decode it once on extraction to avoid double-escaping the same\n * sequences on output.\n */\nfunction unescapePslString(value: string): string {\n let result = '';\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n if (ch !== 0x5c /* '\\\\' */ || i + 1 >= value.length) {\n result += value[i];\n continue;\n }\n const next = value[i + 1];\n if (next === '\\\\' || next === '\"' || next === \"'\") {\n result += next;\n } else if (next === 'n') {\n result += '\\n';\n } else if (next === 'r') {\n result += '\\r';\n } else {\n result += '\\\\';\n result += next;\n }\n i++;\n }\n return result;\n}\n\nfunction modelToPrinterModel(model: PslModel): PrinterModel {\n let mapName: string | undefined;\n const modelAttrStrings: string[] = [];\n\n for (const a of model.attributes) {\n if (a.name === 'map' && a.target === 'model') {\n mapName = getPositionalStringArg(a, 0) ?? mapName;\n continue;\n }\n modelAttrStrings.push(renderPslAttribute(a));\n }\n\n if (mapName !== undefined) {\n modelAttrStrings.push(`@@map(\"${escapePslString(mapName)}\")`);\n }\n\n const printerFields = model.fields.map((f) => fieldToPrinterField(f));\n\n return {\n name: model.name,\n mapName,\n fields: printerFields,\n modelAttributes: modelAttrStrings,\n comment: model.comment,\n };\n}\n\nfunction fieldToPrinterField(field: PslField): PrinterField {\n const typeName =\n field.typeConstructor !== undefined\n ? formatTypeConstructor(field.typeConstructor)\n : field.typeName;\n\n let mapName: string | undefined;\n const attrStrings: string[] = [];\n\n for (const a of field.attributes) {\n if (a.name === 'map' && a.target === 'field') {\n mapName = getPositionalStringArg(a, 0) ?? mapName;\n continue;\n }\n attrStrings.push(renderPslAttribute(a));\n }\n\n if (mapName !== undefined) {\n attrStrings.push(`@map(\"${escapePslString(mapName)}\")`);\n }\n\n const isRelation = field.attributes.some((a) => a.name === 'relation' && a.target === 'field');\n\n const isUnsupported = typeName.startsWith('Unsupported(');\n\n const isId = field.attributes.some((a) => a.name === 'id' && a.target === 'field');\n\n return {\n name: field.name,\n typeName,\n optional: field.optional,\n list: field.list,\n attributes: attrStrings,\n mapName,\n isId,\n isRelation,\n isUnsupported,\n comment: undefined,\n };\n}\n\nfunction buildModelFkDeps(\n models: readonly PslModel[],\n modelNames: ReadonlySet<string>,\n): Map<string, Set<string>> {\n const deps = new Map<string, Set<string>>();\n for (const m of models) {\n deps.set(m.name, new Set());\n }\n\n for (const m of models) {\n for (const field of m.fields) {\n const refModel = relationReferencedModel(field, modelNames);\n if (!refModel || refModel === m.name) continue;\n if (!hasFullRelation(field)) continue;\n (deps.get(m.name) as Set<string>).add(refModel);\n }\n }\n\n return deps;\n}\n\nfunction hasFullRelation(field: PslField): boolean {\n const rel = field.attributes.find((a) => a.name === 'relation' && a.target === 'field');\n if (!rel) return false;\n const named = Object.fromEntries(\n rel.args\n .filter(\n (a): a is import('@prisma-next/framework-components/psl-ast').PslAttributeNamedArgument =>\n a.kind === 'named',\n )\n .map((a) => [a.name, a.value.trim()]),\n );\n return named['fields'] !== undefined && named['references'] !== undefined;\n}\n\nfunction relationReferencedModel(\n field: PslField,\n modelNames: ReadonlySet<string>,\n): string | undefined {\n const head = field.typeConstructor?.path[0];\n const raw = head ?? field.typeName.replace(/\\?$/, '').replace(/\\[\\]$/, '');\n if (raw.length === 0) {\n return undefined;\n }\n return modelNames.has(raw) ? raw : undefined;\n}\n\nfunction topologicalSortModels(\n models: readonly PslModel[],\n deps: ReadonlyMap<string, Set<string>>,\n): PslModel[] {\n const byName = new Map(models.map((m) => [m.name, m]));\n const result: PslModel[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const sortedNames = [...deps.keys()].sort();\n\n function visit(name: string): void {\n if (visited.has(name)) return;\n if (visiting.has(name)) return;\n visiting.add(name);\n\n const sortedDeps = [...(deps.get(name) ?? new Set())].sort();\n for (const dep of sortedDeps) {\n visit(dep);\n }\n\n visiting.delete(name);\n visited.add(name);\n const model = byName.get(name);\n if (model) {\n result.push(model);\n }\n }\n\n for (const name of sortedNames) {\n visit(name);\n }\n\n return result;\n}\n","import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';\nimport { astDocumentToPrintDocument } from './ast-to-print-document';\nimport { serializePrintDocument } from './serialize-print-document';\n\nexport function printPslFromAst(ast: PslDocumentAst): string {\n const doc = astDocumentToPrintDocument(ast);\n return serializePrintDocument(doc);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@prisma-next/psl-printer",
3
- "version": "0.5.0-dev.66",
3
+ "version": "0.5.0-dev.68",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "PSL printer: deterministic Prisma Schema Language (.prisma) output from a PslDocumentAst",
8
8
  "dependencies": {
9
- "@prisma-next/framework-components": "0.5.0-dev.66"
9
+ "@prisma-next/framework-components": "0.5.0-dev.68"
10
10
  },
11
11
  "devDependencies": {
12
- "tsdown": "0.18.4",
12
+ "tsdown": "0.22.0",
13
13
  "typescript": "5.9.3",
14
- "vitest": "4.0.17",
15
- "@prisma-next/psl-parser": "0.5.0-dev.66",
14
+ "vitest": "4.1.5",
16
15
  "@prisma-next/tsdown": "0.0.0",
17
- "@prisma-next/tsconfig": "0.0.0"
16
+ "@prisma-next/tsconfig": "0.0.0",
17
+ "@prisma-next/psl-parser": "0.5.0-dev.68"
18
18
  },
19
19
  "files": [
20
20
  "dist",