Skip to content

Commit

Permalink
Unify code-base as ts and transpile to cjs for publish
Browse files Browse the repository at this point in the history
  • Loading branch information
jdalton committed Jul 25, 2024
1 parent da75762 commit 6554f0f
Show file tree
Hide file tree
Showing 126 changed files with 14,785 additions and 9,088 deletions.
39 changes: 39 additions & 0 deletions .config/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict'

const { isEsmId } = require('../scripts/packages')

module.exports = {
plugins: [
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-transform-export-namespace-from',
[
'@babel/plugin-transform-runtime',
{
absoluteRuntime: false,
corejs: false,
helpers: true,
regenerator: false,
version: '^7.24.6'
}
],
[
'@babel/plugin-transform-modules-commonjs',
{
allowTopLevelThis: true,
importInterop: (specifier, requestingFilename) => {
if (requestingFilename) {
const specIsEsm = isEsmId(specifier, requestingFilename)
const parentIsEsm = isEsmId(requestingFilename)
if (specIsEsm && parentIsEsm) {
return 'none'
}
if (specIsEsm) {
return 'babel'
}
}
return 'node'
}
}
]
]
}
229 changes: 229 additions & 0 deletions .config/rollup.base.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { builtinModules, createRequire } from 'node:module'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import commonjs from '@rollup/plugin-commonjs'
import replace from '@rollup/plugin-replace'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import rangesIntersect from 'semver/ranges/intersects.js'
import { readPackageUpSync } from 'read-package-up'

import { loadJSON } from '../scripts/files.js'
import {
getPackageName,
getPackageNameEnd,
isEsmId,
normalizeId,
isPackageName,
isBuiltin,
resolveId
} from '../scripts/packages.js'
import { escapeRegExp } from '../scripts/strings.js'
import socketModifyPlugin from '../scripts/rollup/socket-modify-plugin.js'

const __dirname = fileURLToPath(new URL('.', import.meta.url))
const require = createRequire(import.meta.url)

const ts = require('rollup-plugin-ts')

const ENTRY_SUFFIX = '?commonjs-entry'
const EXTERNAL_SUFFIX = '?commonjs-external'

const builtinAliases = builtinModules.reduce((o, n) => {
o[n] = `node:${n}`
return o
}, {})

const rootPath = path.resolve(__dirname, '..')
const babelConfigPath = path.join(__dirname, 'babel.config.js')
const tsconfigPath = path.join(__dirname, 'tsconfig.rollup.json')

const babelConfig = require(babelConfigPath)
const { dependencies: pkgDeps, devDependencies: pkgDevDeps } = loadJSON(
path.resolve(rootPath, 'package.json')
)

const customResolver = nodeResolve({
exportConditions: ['node'],
preferBuiltins: true
})

export default (extendConfig = {}) => {
const depStats = {
dependencies: { __proto__: null },
devDependencies: { __proto__: null },
esm: { __proto__: null },
external: { __proto__: null },
transitives: { __proto__: null }
}

const config = {
__proto__: {
meta: {
depStats
}
},
external(id_, parentId_) {
if (id_.endsWith(EXTERNAL_SUFFIX) || isBuiltin(id_)) {
return true
}
const id = normalizeId(id_)
if (id.endsWith('.cjs') || id.endsWith('.json')) {
return true
}
if (
id.endsWith('.mjs') ||
id.endsWith('.mts') ||
id.endsWith('.ts') ||
!isPackageName(id)
) {
return false
}
const name = getPackageName(id)
const parentId = parentId_ ? resolveId(parentId_) : undefined
const resolvedId = resolveId(id, parentId)
if (isEsmId(resolvedId, parentId)) {
const parentPkg = parentId
? readPackageUpSync({ cwd: path.dirname(parentId) })?.packageJson
: undefined
depStats.esm[name] =
pkgDeps[name] ??
pkgDevDeps[name] ??
parentPkg?.dependencies?.[name] ??
parentPkg?.optionalDependencies?.[name] ??
parentPkg?.peerDependencies?.[name] ??
readPackageUpSync({ cwd: path.dirname(resolvedId) })?.packageJson
?.version ??
''
return false
}
const parentNodeModulesIndex = parentId.lastIndexOf('/node_modules/')
if (parentNodeModulesIndex !== -1) {
const parentNameStart = parentNodeModulesIndex + 14
const parentNameEnd = getPackageNameEnd(parentId, parentNameStart)
const {
version,
dependencies = {},
optionalDependencies = {},
peerDependencies = {}
} = loadJSON(`${parentId.slice(0, parentNameEnd)}/package.json`)
const curRange =
dependencies[name] ??
optionalDependencies[name] ??
peerDependencies[name] ??
version
const seenRange = pkgDeps[name] ?? depStats.external[name]
if (seenRange) {
return rangesIntersect(seenRange, curRange)
}
depStats.external[name] = curRange
depStats.transitives[name] = curRange
} else if (pkgDeps[name]) {
depStats.external[name] = pkgDeps[name]
depStats.dependencies[name] = pkgDeps[name]
} else if (pkgDevDeps[name]) {
depStats.devDependencies[name] = pkgDevDeps[name]
}
return true
},
...extendConfig,
plugins: [
customResolver,
ts({
transpiler: 'babel',
browserslist: false,
transpileOnly: true,
exclude: ['**/*.json'],
babelConfig,
tsconfig: tsconfigPath
}),
// Convert un-prefixed built-in imports into "node:"" prefixed forms.
replace({
delimiters: ['(?<=(?:require\\(|from\\s*)["\'])', '(?=["\'])'],
preventAssignment: false,
values: builtinAliases
}),
// Convert `require('u' + 'rl')` into something like `require$$2$3`.
socketModifyPlugin({
find: /require\('u' \+ 'rl'\)/g,
replace(match) {
return (
/(?<=var +)[$\w]+(?= *= *require\('node:url'\))/.exec(
this.input
)?.[0] ?? match
)
}
}),
// Remove bare require calls, e.g. require calls not associated with an
// import binding:
// require('node:util')
// require('graceful-fs')
socketModifyPlugin({
find: /^\s*require\(["'].+?["']\);?\r?\n/gm,
replace: ''
}),
// Fix incorrectly set "spinners" binding caused by a transpilation bug
// https://github.com/sindresorhus/ora/blob/main/index.js#L416C2-L416C50
// export {default as spinners} from 'cli-spinners'
socketModifyPlugin({
find: /(?<=ora[^.]+\.spinners\s*=\s*)[$\w]+/g,
replace(match) {
return (
new RegExp(`(?<=${escapeRegExp(match)}\\s*=\\s*)[$\\w]+`).exec(
this.input
)?.[0] ?? match
)
}
}),
commonjs({
ignoreDynamicRequires: true,
ignoreGlobal: true,
ignoreTryCatch: true,
defaultIsModuleExports: true,
transformMixedEsModules: true,
extensions: ['.cjs', '.js', '.ts', `.ts${ENTRY_SUFFIX}`]
}),
...(extendConfig.plugins ?? [])
]
}

const output = (
Array.isArray(config.output)
? config.output
: config.output
? [config.output]
: []
).map(o => ({
...o,
chunkFileNames: '[name].js',
manualChunks(id) {
if (id.includes('/node_modules/')) {
return 'vendor'
}
}
}))

// Replace hard-coded absolute paths in source with hard-coded relative paths.
const replacePlugin = replace({
delimiters: ['(?<=["\'])', '/'],
preventAssignment: false,
values: {
[rootPath]: '../'
}
})

const replaceOutputPlugin = {
name: replacePlugin.name,
renderChunk: replacePlugin.renderChunk
}

for (const o of output) {
o.plugins = [
...(Array.isArray(o.plugins) ? o.plugins : []),
replaceOutputPlugin
]
}

config.output = output
return config
}
102 changes: 102 additions & 0 deletions .config/rollup.dist.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { chmodSync, readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import { loadJSON } from '../scripts/files.js'
import { hasKeys } from '../scripts/objects.js'
import { toSortedObject } from '../scripts/sorts.js'
import { formatObject } from '../scripts/strings.js'

import baseConfig from './rollup.base.config.mjs'

const __dirname = fileURLToPath(new URL('.', import.meta.url))

const rootPath = path.resolve(__dirname, '..')
const depStatsPath = path.join(rootPath, '.dep-stats.json')
const distPath = path.join(rootPath, 'dist')
const srcPath = path.join(rootPath, 'src')

const pkgJSONPath = path.resolve(rootPath, 'package.json')
const pkgJSON = loadJSON(pkgJSONPath)

export default () => {
const config = baseConfig({
input: {
cli: `${srcPath}/cli.ts`,
'npm-cli': `${srcPath}/shadow/npm-cli.ts`,
'npx-cli': `${srcPath}/shadow/npx-cli.ts`,
'npm-injection': `${srcPath}/shadow/npm-injection.ts`
},
output: [
{
dir: 'dist',
entryFileNames: '[name].js',
format: 'cjs',
exports: 'auto',
externalLiveBindings: false,
freeze: false
}
],
plugins: [
{
writeBundle() {
const { '@cyclonedx/cdxgen': cdxgenRange, synp: synpRange } =
pkgJSON.dependencies
const { depStats } = config.meta

// Manually add @cyclonedx/cdxgen and synp as they are not directly
// referenced in the code but used through spawned processes.
depStats.dependencies['@cyclonedx/cdxgen'] = cdxgenRange
depStats.dependencies.synp = synpRange
depStats.external['@cyclonedx/cdxgen'] = cdxgenRange
depStats.external.synp = synpRange

try {
// Remove transitives from dependencies
const oldDepStats = loadJSON(depStatsPath)
for (const key of Object.keys(oldDepStats.transitives)) {
if (pkgJSON.dependencies[key]) {
depStats.transitives[key] = pkgJSON.dependencies[key]
depStats.external[key] = pkgJSON.dependencies[key]
delete depStats.dependencies[key]
}
}
} catch {}

depStats.dependencies = toSortedObject(depStats.dependencies)
depStats.devDependencies = toSortedObject(depStats.devDependencies)
depStats.esm = toSortedObject(depStats.esm)
depStats.external = toSortedObject(depStats.external)
depStats.transitives = toSortedObject(depStats.transitives)

// Write dep stats
writeFileSync(depStatsPath, `${formatObject(depStats)}\n`, 'utf8')

// Make dist files chmod +x
chmodSync(path.join(distPath, 'cli.js'), 0o755)
chmodSync(path.join(distPath, 'npm-cli.js'), 0o755)
chmodSync(path.join(distPath, 'npx-cli.js'), 0o755)

// Update dependencies with additional inlined modules
writeFileSync(
pkgJSONPath,
readFileSync(pkgJSONPath, 'utf8').replace(
/(?<="dependencies":\s*)\{[^\}]*\}/,
() => {
const deps = {
...depStats.dependencies,
...depStats.transitives
}
const formatted = formatObject(deps, 4)
return hasKeys(deps) ? formatted.replace('}', ' }') : formatted
}
),
'utf8'
)
}
}
]
})

return config
}
26 changes: 26 additions & 0 deletions .config/rollup.test.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import baseConfig from './rollup.base.config.mjs'

const __dirname = fileURLToPath(new URL('.', import.meta.url))

const rootPath = path.resolve(__dirname, '..')
const srcPath = path.join(rootPath, 'src')

export default () =>
baseConfig({
input: {
'path-resolve': `${srcPath}/utils/path-resolve.ts`
},
output: [
{
dir: 'test/dist',
entryFileNames: '[name].js',
format: 'cjs',
exports: 'auto',
externalLiveBindings: false,
freeze: false
}
]
})
Loading

0 comments on commit 6554f0f

Please sign in to comment.