Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pg-protocol] patch out the const enum in messages.d.ts #2489

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pg-protocol/package.json
Expand Up @@ -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"
Expand Down
120 changes: 120 additions & 0 deletions 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')
}
}
}