build(core): improved tree-shakeability
This commit is contained in:
parent
56b2fe70d3
commit
42f1482d7f
5 changed files with 68 additions and 5 deletions
|
@ -1,4 +1,6 @@
|
|||
module.exports = ({ path, transformFile, packageDir, outDir }) => ({
|
||||
const KNOWN_DECORATORS = ['memoizeGetters', 'makeInspectable']
|
||||
|
||||
module.exports = ({ path, glob, transformFile, packageDir, outDir }) => ({
|
||||
esmOnlyDirectives: true,
|
||||
esmImportDirectives: true,
|
||||
final() {
|
||||
|
@ -7,5 +9,56 @@ module.exports = ({ path, transformFile, packageDir, outDir }) => ({
|
|||
|
||||
transformFile(path.join(outDir, 'cjs/network/network-manager.js'), replaceVersion)
|
||||
transformFile(path.join(outDir, 'esm/network/network-manager.js'), replaceVersion)
|
||||
|
||||
// make decorators properly tree-shakeable
|
||||
// very fragile, but it works for now :D
|
||||
const decoratorsRegex = new RegExp(
|
||||
`(${KNOWN_DECORATORS.join('|')})\\((.+?)\\);`,
|
||||
'gs',
|
||||
)
|
||||
|
||||
const replaceDecorators = (content, file) => {
|
||||
if (!KNOWN_DECORATORS.some((d) => content.includes(d))) return null
|
||||
|
||||
const countPerClass = new Map()
|
||||
|
||||
content = content.replace(decoratorsRegex, (_, name, args) => {
|
||||
const [clsName_, ...rest] = args.split(',')
|
||||
const clsName = clsName_.trim()
|
||||
|
||||
const count = (countPerClass.get(clsName) || 0) + 1
|
||||
countPerClass.set(clsName, count)
|
||||
|
||||
const prevName = count === 1 ? clsName : `${clsName}$${count - 1}`
|
||||
const localName = `${clsName}$${count}`
|
||||
|
||||
return `const ${localName} = /*#__PURE__*/${name}(${prevName}, ${rest.join(',')});`
|
||||
})
|
||||
|
||||
if (countPerClass.size === 0) {
|
||||
throw new Error('No decorator usages found, but known names were used')
|
||||
}
|
||||
|
||||
const customExports = []
|
||||
|
||||
for (const [clsName, count] of countPerClass) {
|
||||
const needle = new RegExp(`^export class(?= ${clsName} ({|extends ))`, 'm')
|
||||
|
||||
if (!content.match(needle)) {
|
||||
throw new Error(`Class ${clsName} not found in ${file}`)
|
||||
}
|
||||
|
||||
content = content.replace(needle, 'class')
|
||||
customExports.push(
|
||||
`export { ${clsName}$${count} as ${clsName} }`,
|
||||
)
|
||||
}
|
||||
|
||||
return content + '\n' + customExports.join('\n') + '\n'
|
||||
}
|
||||
|
||||
for (const f of glob.sync(path.join(outDir, 'esm/highlevel/types/**/*.js'))) {
|
||||
transformFile(f, replaceDecorators)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
@ -33,7 +33,11 @@ function getAllGettersNames<T>(obj: T): (keyof T)[] {
|
|||
* > (getter that caches after its first invocation is also
|
||||
* > considered pure in this case)
|
||||
*/
|
||||
export function makeInspectable<T>(obj: new (...args: any[]) => T, props?: (keyof T)[], hide?: (keyof T)[]): void {
|
||||
export function makeInspectable<T>(
|
||||
obj: new (...args: any[]) => T,
|
||||
props?: (keyof T)[],
|
||||
hide?: (keyof T)[],
|
||||
): typeof obj {
|
||||
const getters: (keyof T)[] = props ? props : []
|
||||
|
||||
for (const key of getAllGettersNames<T>(obj.prototype)) {
|
||||
|
@ -67,4 +71,6 @@ export function makeInspectable<T>(obj: new (...args: any[]) => T, props?: (keyo
|
|||
return ret
|
||||
}
|
||||
obj.prototype[customInspectSymbol] = obj.prototype.toJSON
|
||||
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function memoizeGetters<T>(cls: new (...args: any[]) => T, fields: (keyof T)[]) {
|
||||
export function memoizeGetters<T>(cls: new (...args: any[]) => T, fields: (keyof T)[]): typeof cls {
|
||||
for (const field of fields) {
|
||||
const desc = Object.getOwnPropertyDescriptor(cls.prototype, field)
|
||||
if (!desc) continue
|
||||
|
@ -25,4 +25,6 @@ export function memoizeGetters<T>(cls: new (...args: any[]) => T, fields: (keyof
|
|||
configurable: true,
|
||||
})
|
||||
}
|
||||
|
||||
return cls
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ function writeQuery(query: InputQuery) {
|
|||
return `?${str}`
|
||||
}
|
||||
|
||||
export function deeplinkBuilder<T>(params: BuildDeeplinkOptions<T>): Deeplink<T> {
|
||||
/* @__NO_SIDE_EFFECTS__ */ export function deeplinkBuilder<T>(params: BuildDeeplinkOptions<T>): Deeplink<T> {
|
||||
const { internalBuild, internalParse, externalBuild, externalParse } = params
|
||||
|
||||
const fn_ = (options: T & CommonDeeplinkOptions) => {
|
||||
|
|
|
@ -17,7 +17,8 @@ function exec(cmd, params) {
|
|||
|
||||
function transformFile(file, transform) {
|
||||
const content = fs.readFileSync(file, 'utf8')
|
||||
fs.writeFileSync(file, transform(content))
|
||||
const res = transform(content, file)
|
||||
if (res != null) fs.writeFileSync(file, res)
|
||||
}
|
||||
|
||||
const buildConfig = {
|
||||
|
@ -44,6 +45,7 @@ const buildConfig = {
|
|||
config = config({
|
||||
fs,
|
||||
path,
|
||||
glob,
|
||||
exec,
|
||||
transformFile,
|
||||
packageDir,
|
||||
|
|
Loading…
Reference in a new issue