diff --git a/packages/pg-protocol/package.json b/packages/pg-protocol/package.json index 05f74ae10..c3595e853 100644 --- a/packages/pg-protocol/package.json +++ b/packages/pg-protocol/package.json @@ -17,7 +17,7 @@ }, "scripts": { "test": "mocha dist/**/*.test.js", - "build": "tsc", + "build": "tsc && node script/remove-public-const-enum.js", "build:watch": "tsc --watch", "prepublish": "yarn build", "pretest": "yarn build" diff --git a/packages/pg-protocol/script/remove-public-const-enum.js b/packages/pg-protocol/script/remove-public-const-enum.js new file mode 100644 index 000000000..d8536df90 --- /dev/null +++ b/packages/pg-protocol/script/remove-public-const-enum.js @@ -0,0 +1,120 @@ +// This task would be better served by recast or something like that. +// https://github.com/travellocal/babel-plugin-declare-const-enum is a good starting point for that. + +const fs = require('fs') +const path = require('path') + +const PATCH_SOURCE = process.argv.slice(2).indexOf('--src') >= 0 +const filepath = path.join(__dirname, PATCH_SOURCE ? '../src/messages.ts' : '../dist/messages.d.ts') +const backuppath = path.join( + __dirname, + PATCH_SOURCE ? '../src/messages.const-enum.ts' : '../dist/messages.const-enum.d.ts' +) +const otherfiles = ['../src/parser.ts'] +/** @type {string} */ +let srcpath +if (PATCH_SOURCE) { + srcpath = filepath +} else { + // use the filepath if it's newer + try { + const backupStat = fs.statSync(backuppath) + const fileStat = fs.statSync(filepath) + srcpath = fileStat.mtimeMs > backupStat.mtimeMs ? filepath : backuppath + } catch (err) { + if (err.code !== 'ENOENT') { + throw err + } + srcpath = filepath + } +} +const src = fs.readFileSync(srcpath, 'utf8') + +/** @type {({startIndex: number, endIndex: number, content: string})[]} */ +let replacements = [] + +// find the const enum declarations +const startRe = PATCH_SOURCE + ? /(^|\n)export const enum ([A-Za-z][A-Za-z0-9]+) \{\n*/g + : /(^|\n)export declare const enum ([A-Za-z][A-Za-z0-9]+) \{\n*/g +const endRe = /\n\}/g + +const constEnums = {} + +/** @type {RegExpExecArray | null} */ +let match +while ((match = startRe.exec(src))) { + const startIndex = match.index + const name = match[2] + const contentStartIndex = (endRe.lastIndex = startRe.lastIndex) + const end = endRe.exec(src) + if (!end) break + const contentEndIndex = end.index + const endIndex = (startRe.lastIndex = endRe.lastIndex) + + // collect the members of the const enum + const constEnumContent = src.slice(contentStartIndex, contentEndIndex) + const itemRe = /\b([A-Za-z][A-Za-z0-9]+)\s*=\s*(.+),/g + const lastRe = /\b([A-Za-z][A-Za-z0-9]+)\s*=\s*(.+)/g + + const enumItems = (constEnums[name] = {}) + const enumValueLiterals = [] + + /** @type {RegExpExecArray | null} */ + let itemMatch + while ((itemMatch = itemRe.exec(constEnumContent))) { + enumValueLiterals.push((enumItems[itemMatch[1]] = itemMatch[2])) + lastRe.lastIndex = itemRe.lastIndex + } + itemMatch = lastRe.exec(constEnumContent) + if (itemMatch) { + enumValueLiterals.push((enumItems[itemMatch[1]] = itemMatch[2])) + } + + replacements.push({ + startIndex, + endIndex, + content: `${match[1]}export type ${name} =\n${enumValueLiterals.map((s) => ` | ${s}`).join('\n')};`, + }) +} + +if (replacements.length > 0) { + // replace the const enum declarations with a literal type union + let out = replacements + .sort((a, b) => a.startIndex - b.startIndex) + .reduce((out, { endIndex, content }, i, r) => { + const next = r[i + 1] + return out + content + src.slice(endIndex, next && next.startIndex) + }, src.slice(0, replacements[0].startIndex)) + + // replace references to the enum with the literals + for (const enumName of Object.keys(constEnums)) { + const enumItems = constEnums[enumName] + const re = new RegExp(`\\b${enumName}\\.(${Object.keys(enumItems).join('|')})\\b`, 'g') + + out = out.replace(re, (s, enumItemName) => enumItems[enumItemName]) + } + + if (!PATCH_SOURCE && srcpath === filepath) { + fs.writeFileSync(backuppath, src, 'utf8') + } + fs.writeFileSync(filepath, out, 'utf8') + if (!PATCH_SOURCE) { + const now = new Date() + fs.utimesSync(backuppath, now, now) + } else { + for (const f of otherfiles) { + const otherfile = path.join(__dirname, f) + + let otherOut = fs.readFileSync(otherfile, 'utf8') + // replace references to the enum with the literals + for (const enumName of Object.keys(constEnums)) { + const enumItems = constEnums[enumName] + const re = new RegExp(`\\b${enumName}\\.(${Object.keys(enumItems).join('|')})\\b`, 'g') + + otherOut = otherOut.replace(re, (s, enumItemName) => enumItems[enumItemName]) + } + fs.writeFileSync(otherfile, otherOut, 'utf8') + } + } +}