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

fix: build promise resolves before types are emitted #597

Merged
merged 3 commits into from Apr 1, 2022
Merged
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
298 changes: 154 additions & 144 deletions src/index.ts
Expand Up @@ -144,165 +144,175 @@ export async function build(_options: Options) {
logger.info('CLI', 'Running in watch mode')
}

if (options.dts) {
const worker = new Worker(path.join(__dirname, './rollup.js'))
worker.postMessage({
configName: item?.name,
options: {
...options, // functions cannot be cloned
banner: undefined,
footer: undefined,
esbuildPlugins: undefined,
esbuildOptions: undefined,
plugins: undefined,
},
})
worker.on('message', (data) => {
if (data === 'error') {
process.exitCode = 1
} else if (data === 'success') {
process.exitCode = 0
}
})
const dtsTask = async () => {
if (options.dts) {
await new Promise<void>((resolve, reject) => {
const worker = new Worker(path.join(__dirname, './rollup.js'))
worker.postMessage({
configName: item?.name,
options: {
...options, // functions cannot be cloned
banner: undefined,
footer: undefined,
esbuildPlugins: undefined,
esbuildOptions: undefined,
plugins: undefined,
},
})
worker.on('message', (data) => {
if (data === 'error') {
process.exitCode = 1
reject()
} else if (data === 'success') {
process.exitCode = 0
resolve()
}
})
})
}
}

if (!options.dts?.only) {
let existingOnSuccess: ChildProcess | undefined
/** Files imported by the entry */
const buildDependencies: Set<string> = new Set()
const otherTasks = async () => {
if (!options.dts?.only) {
let existingOnSuccess: ChildProcess | undefined
/** Files imported by the entry */
const buildDependencies: Set<string> = new Set()

const killPreviousProcess = async () => {
if (existingOnSuccess) {
await killProcess({
pid: existingOnSuccess.pid,
})
existingOnSuccess = undefined
const killPreviousProcess = async () => {
if (existingOnSuccess) {
await killProcess({
pid: existingOnSuccess.pid,
})
existingOnSuccess = undefined
}
}
}

const debouncedBuildAll = debouncePromise(
() => {
return buildAll()
},
100,
handleError
)

const buildAll = async () => {
const killPromise = killPreviousProcess()
// Store previous build dependencies in case the build failed
// So we can restore it
const previousBuildDependencies = new Set(buildDependencies)
buildDependencies.clear()

if (options.clean) {
const extraPatterns = Array.isArray(options.clean)
? options.clean
: []
await removeFiles(
['**/*', '!**/*.d.ts', ...extraPatterns],
options.outDir
)
logger.info('CLI', 'Cleaning output folder')
}
const debouncedBuildAll = debouncePromise(
() => {
return buildAll()
},
100,
handleError
)

const css: Map<string, string> = new Map()
await Promise.all([
...options.format.map(async (format, index) => {
const pluginContainer = new PluginContainer([
shebang(),
...(options.plugins || []),
cjsSplitting(),
es5(),
sizeReporter(),
])
await pluginContainer.buildStarted()
await runEsbuild(options, {
pluginContainer,
format,
css: index === 0 || options.injectStyle ? css : undefined,
logger,
buildDependencies,
}).catch((error) => {
previousBuildDependencies.forEach((v) =>
buildDependencies.add(v)
)
throw error
const buildAll = async () => {
const killPromise = killPreviousProcess()
// Store previous build dependencies in case the build failed
// So we can restore it
const previousBuildDependencies = new Set(buildDependencies)
buildDependencies.clear()

if (options.clean) {
const extraPatterns = Array.isArray(options.clean)
? options.clean
: []
await removeFiles(
['**/*', '!**/*.d.ts', ...extraPatterns],
options.outDir
)
logger.info('CLI', 'Cleaning output folder')
}

const css: Map<string, string> = new Map()
await Promise.all([
...options.format.map(async (format, index) => {
const pluginContainer = new PluginContainer([
shebang(),
...(options.plugins || []),
cjsSplitting(),
es5(),
sizeReporter(),
])
await pluginContainer.buildStarted()
await runEsbuild(options, {
pluginContainer,
format,
css: index === 0 || options.injectStyle ? css : undefined,
logger,
buildDependencies,
}).catch((error) => {
previousBuildDependencies.forEach((v) =>
buildDependencies.add(v)
)
throw error
})
}),
])
await killPromise
if (options.onSuccess) {
const parts = parseArgsStringToArgv(options.onSuccess)
const exec = parts[0]
const args = parts.splice(1)
existingOnSuccess = execa(exec, args, {
stdio: 'inherit',
})
}),
])
await killPromise
if (options.onSuccess) {
const parts = parseArgsStringToArgv(options.onSuccess)
const exec = parts[0]
const args = parts.splice(1)
existingOnSuccess = execa(exec, args, {
stdio: 'inherit',
})
}
}
}

const startWatcher = async () => {
if (!options.watch) return

const { watch } = await import('chokidar')

const customIgnores = options.ignoreWatch
? Array.isArray(options.ignoreWatch)
? options.ignoreWatch
: [options.ignoreWatch]
: []

const ignored = [
'**/{.git,node_modules}/**',
options.outDir,
...customIgnores,
]

const watchPaths =
typeof options.watch === 'boolean'
? '.'
: Array.isArray(options.watch)
? options.watch.filter(
(path): path is string => typeof path === 'string'
)
: options.watch

logger.info(
'CLI',
`Watching for changes in ${
Array.isArray(watchPaths)
? watchPaths.map((v) => '"' + v + '"').join(' | ')
: '"' + watchPaths + '"'
}`
)
logger.info(
'CLI',
`Ignoring changes in ${ignored
.map((v) => '"' + v + '"')
.join(' | ')}`
)
const startWatcher = async () => {
if (!options.watch) return

const watcher = watch(watchPaths, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored,
})
watcher.on('all', (type, file) => {
file = slash(file)
if (!buildDependencies.has(file)) return
const { watch } = await import('chokidar')

logger.info('CLI', `Change detected: ${type} ${file}`)
debouncedBuildAll()
})
}
const customIgnores = options.ignoreWatch
? Array.isArray(options.ignoreWatch)
? options.ignoreWatch
: [options.ignoreWatch]
: []

logger.info('CLI', `Target: ${options.target}`)
const ignored = [
'**/{.git,node_modules}/**',
options.outDir,
...customIgnores,
]

const watchPaths =
typeof options.watch === 'boolean'
? '.'
: Array.isArray(options.watch)
? options.watch.filter(
(path): path is string => typeof path === 'string'
)
: options.watch

logger.info(
'CLI',
`Watching for changes in ${
Array.isArray(watchPaths)
? watchPaths.map((v) => '"' + v + '"').join(' | ')
: '"' + watchPaths + '"'
}`
)
logger.info(
'CLI',
`Ignoring changes in ${ignored
.map((v) => '"' + v + '"')
.join(' | ')}`
)

await buildAll()
const watcher = watch(watchPaths, {
ignoreInitial: true,
ignorePermissionErrors: true,
ignored,
})
watcher.on('all', (type, file) => {
file = slash(file)
if (!buildDependencies.has(file)) return

startWatcher()
logger.info('CLI', `Change detected: ${type} ${file}`)
debouncedBuildAll()
})
}

logger.info('CLI', `Target: ${options.target}`)

await buildAll()

startWatcher()
}
}

await Promise.all([dtsTask(), otherTasks()])
}
)
)
Expand Down
1 change: 1 addition & 0 deletions src/rollup.ts
Expand Up @@ -244,6 +244,7 @@ const startRollup = async (options: NormalizedOptions) => {
watchRollup(config)
} else {
await runRollup(config)
parentPort?.postMessage('success')
parentPort?.close()
}
}
Expand Down