Skip to content

Commit

Permalink
Update externals stubs atomically
Browse files Browse the repository at this point in the history
Since babel can run in many workers, and since we write the external stubs as a side-effect of discovering that they're needed from within babel, it's possible that a stub is written at the exact moment another worker is reading it. So we need to make sure our writes are atomic.
  • Loading branch information
ef4 committed Mar 3, 2022
1 parent be9de27 commit 843784d
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 3 deletions.
14 changes: 14 additions & 0 deletions packages/core/src/atomic-write.ts
@@ -0,0 +1,14 @@
import { randomBytes } from 'crypto';
import { outputFileSync } from 'fs-extra';
import { renameSync } from 'fs';

// if the path already exists, this will replace it atomically so that nobody
// will ever read an inconsistent state.
//
// if it doesn't already exist, this will create any intervening directories and
// then create the file.
export function atomicWrite(path: string, content: string) {
let suffix = randomBytes(8).toString('hex');
outputFileSync(path + suffix, content);
renameSync(path + suffix, path);
}
6 changes: 3 additions & 3 deletions packages/core/src/babel-plugin-adjust-imports.ts
Expand Up @@ -4,11 +4,11 @@ import type { NodePath } from '@babel/traverse';
import type * as Babel from '@babel/core';
import type { types as t } from '@babel/core';
import { PackageCache, Package, V2Package, explicitRelative } from '@embroider/shared-internals';
import { outputFileSync } from 'fs-extra';
import { Memoize } from 'typescript-memoize';
import { compile } from './js-handlebars';
import { handleImportDeclaration } from './mini-modules-polyfill';
import { ImportUtil } from 'babel-import-util';
import { atomicWrite } from './atomic-write';

interface State {
adjustFile: AdjustFile;
Expand Down Expand Up @@ -333,7 +333,7 @@ function handleExternal(specifier: string, sourceFile: AdjustFile, opts: Options

function makeMissingModule(specifier: string, sourceFile: AdjustFile, opts: Options): string {
let target = join(opts.externalsDir, specifier + '.js');
outputFileSync(
atomicWrite(
target,
dynamicMissingModule({
moduleName: specifier,
Expand All @@ -344,7 +344,7 @@ function makeMissingModule(specifier: string, sourceFile: AdjustFile, opts: Opti

function makeExternal(specifier: string, sourceFile: AdjustFile, opts: Options): string {
let target = join(opts.externalsDir, specifier + '.js');
outputFileSync(
atomicWrite(
target,
externalTemplate({
runtimeName: specifier,
Expand Down

0 comments on commit 843784d

Please sign in to comment.