Skip to content

Commit

Permalink
fix(ruleset-bundler): builtins plugin should create a new instance fo…
Browse files Browse the repository at this point in the history
…r each module
  • Loading branch information
P0lip committed Feb 25, 2022
1 parent 0263bf0 commit b06903c
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 45 deletions.
129 changes: 96 additions & 33 deletions packages/ruleset-bundler/src/plugins/__tests__/builtins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ import { builtins } from '../builtins';

describe('Builtins Plugin', () => {
let io: IO;
let randomSpy: jest.SpyInstance;

beforeEach(() => {
io = {
fs,
fetch: runtime.fetch,
};

randomSpy = jest
.spyOn(Math, 'random')
.mockReturnValueOnce(0.8229275205939697)
.mockReturnValueOnce(0.7505242801973444)
.mockReturnValueOnce(0.5647855410879519);
});

afterEach(() => {
randomSpy.mockRestore();
});

describe.each<BundleOptions['target']>(['browser', 'runtime'])('given %s target', target => {
Expand Down Expand Up @@ -51,21 +62,21 @@ export default {
});

expect(code)
.toEqual(`const alphabetical = globalThis[Symbol.for('@stoplight/spectral-functions')]['alphabetical'];
const casing = globalThis[Symbol.for('@stoplight/spectral-functions')]['casing'];
const defined = globalThis[Symbol.for('@stoplight/spectral-functions')]['defined'];
const enumeration = globalThis[Symbol.for('@stoplight/spectral-functions')]['enumeration'];
const falsy = globalThis[Symbol.for('@stoplight/spectral-functions')]['falsy'];
const length = globalThis[Symbol.for('@stoplight/spectral-functions')]['length'];
const pattern = globalThis[Symbol.for('@stoplight/spectral-functions')]['pattern'];
const schema = globalThis[Symbol.for('@stoplight/spectral-functions')]['schema'];
const truthy = globalThis[Symbol.for('@stoplight/spectral-functions')]['truthy'];
const undefined$1 = globalThis[Symbol.for('@stoplight/spectral-functions')]['undefined'];
const unreferencedReusableObject = globalThis[Symbol.for('@stoplight/spectral-functions')]['unreferencedReusableObject'];
const xor = globalThis[Symbol.for('@stoplight/spectral-functions')]['xor'];
const oas = globalThis[Symbol.for('@stoplight/spectral-rulesets')]['oas'];
const asyncapi = globalThis[Symbol.for('@stoplight/spectral-rulesets')]['asyncapi'];
.toEqual(`const alphabetical = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['alphabetical'];
const casing = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['casing'];
const defined = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['defined'];
const enumeration = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['enumeration'];
const falsy = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['falsy'];
const length = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['length'];
const pattern = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['pattern'];
const schema = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['schema'];
const truthy = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['truthy'];
const undefined$1 = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['undefined'];
const unreferencedReusableObject = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['unreferencedReusableObject'];
const xor = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions']['xor'];
const oas = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-rulesets']['oas'];
const asyncapi = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-rulesets']['asyncapi'];
var input = {
extends: [oas],
Expand All @@ -87,7 +98,9 @@ var input = {
export { input as default };
`);

expect(globalThis[Symbol.for('@stoplight/spectral-functions')]).toStrictEqual(functions);
expect(
globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions'],
).toStrictEqual(functions);
});

it('should support overrides', async () => {
Expand All @@ -113,30 +126,78 @@ readFile();`,
],
});

expect(code).toEqual(`const fetch = globalThis[Symbol.for('@stoplight/spectral-runtime')]['fetch'];
const DEFAULT_REQUEST_OPTIONS = globalThis[Symbol.for('@stoplight/spectral-runtime')]['DEFAULT_REQUEST_OPTIONS'];
const decodeSegmentFragment = globalThis[Symbol.for('@stoplight/spectral-runtime')]['decodeSegmentFragment'];
const printError = globalThis[Symbol.for('@stoplight/spectral-runtime')]['printError'];
const PrintStyle = globalThis[Symbol.for('@stoplight/spectral-runtime')]['PrintStyle'];
const printPath = globalThis[Symbol.for('@stoplight/spectral-runtime')]['printPath'];
const printValue = globalThis[Symbol.for('@stoplight/spectral-runtime')]['printValue'];
const startsWithProtocol = globalThis[Symbol.for('@stoplight/spectral-runtime')]['startsWithProtocol'];
const isAbsoluteRef = globalThis[Symbol.for('@stoplight/spectral-runtime')]['isAbsoluteRef'];
const traverseObjUntilRef = globalThis[Symbol.for('@stoplight/spectral-runtime')]['traverseObjUntilRef'];
const getEndRef = globalThis[Symbol.for('@stoplight/spectral-runtime')]['getEndRef'];
const safePointerToPath = globalThis[Symbol.for('@stoplight/spectral-runtime')]['safePointerToPath'];
const getClosestJsonPath = globalThis[Symbol.for('@stoplight/spectral-runtime')]['getClosestJsonPath'];
const readFile = globalThis[Symbol.for('@stoplight/spectral-runtime')]['readFile'];
const readParsable = globalThis[Symbol.for('@stoplight/spectral-runtime')]['readParsable'];
expect(code)
.toEqual(`const fetch = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['fetch'];
const DEFAULT_REQUEST_OPTIONS = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['DEFAULT_REQUEST_OPTIONS'];
const decodeSegmentFragment = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['decodeSegmentFragment'];
const printError = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['printError'];
const PrintStyle = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['PrintStyle'];
const printPath = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['printPath'];
const printValue = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['printValue'];
const startsWithProtocol = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['startsWithProtocol'];
const isAbsoluteRef = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['isAbsoluteRef'];
const traverseObjUntilRef = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['traverseObjUntilRef'];
const getEndRef = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['getEndRef'];
const safePointerToPath = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['safePointerToPath'];
const getClosestJsonPath = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['getClosestJsonPath'];
const readFile = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['readFile'];
const readParsable = globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime']['readParsable'];
readFile();
`);

expect(globalThis[Symbol.for('@stoplight/spectral-runtime')]).toStrictEqual({
expect(
globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime'],
).toStrictEqual({
...runtime,
readFile,
});
});

it('should isolate each instance', async () => {
serveAssets({
'/tmp/input.js': `import { readFile } from '@stoplight/spectral-runtime';
readFile();`,
});

// eslint-disable-next-line @typescript-eslint/no-empty-function
function readFile(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
function readFile2(): void {}

await bundleRuleset('/tmp/input.js', {
format: 'esm',
target,
plugins: [
builtins({
'@stoplight/spectral-runtime': {
readFile,
},
}),
builtins({
'@stoplight/spectral-runtime': {
readFile: readFile2,
},
}),
virtualFs(io),
],
});

expect(
globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-runtime'],
).toStrictEqual({
...runtime,
readFile,
});

expect(
globalThis[Symbol.for('@stoplight-spectral/builtins')]['750524']['@stoplight/spectral-runtime'],
).toStrictEqual({
...runtime,
readFile: readFile2,
});
});
});

describe('given node target', () => {
Expand Down Expand Up @@ -191,7 +252,9 @@ var input = {
export { input as default };
`);

expect(globalThis[Symbol.for('@stoplight/spectral-functions')]).toStrictEqual(functions);
expect(
globalThis[Symbol.for('@stoplight-spectral/builtins')]['822928']['@stoplight/spectral-functions'],
).toStrictEqual(functions);
});
});
});
32 changes: 20 additions & 12 deletions packages/ruleset-bundler/src/plugins/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ type Module = 'core' | 'formats' | 'functions' | 'parsers' | 'ref-resolver' | 'r
type GlobalModules = Record<`@stoplight/spectral-${Module}`, string>;
type Overrides = Record<keyof GlobalModules, Record<string, unknown>>;

const NAME = '@stoplight-spectral/builtins';

function registerModule(
instanceId: number,
id: keyof GlobalModules,
members: Record<string, unknown>,
overrides: Partial<Overrides>,
): [string, string] {
const actualOverrides = overrides[id];
globalThis[Symbol.for(id)] = actualOverrides ? { ...members, ...actualOverrides } : members;
const instances = (globalThis[Symbol.for(NAME)] ??= {}) as Record<string, Partial<Overrides>>;
const root = (instances[instanceId] ??= {});

root[id] = actualOverrides ? { ...members, ...actualOverrides } : members;

const m = `globalThis[Symbol.for('${id}')]`;
const m = `globalThis[Symbol.for('${NAME}')]['${instanceId}']['${id}']`;
let code = '';
for (const member of Object.keys(members)) {
code += `export const ${member} = ${m}['${member}'];\n`;
Expand All @@ -29,26 +35,28 @@ function registerModule(
}

export const builtins = (overrides: Partial<Overrides> = {}): Plugin => {
const instanceId = Math.round(Math.random() * 1_000_000);

const modules = Object.fromEntries([
registerModule('@stoplight/spectral-core', core, overrides),
registerModule('@stoplight/spectral-formats', formats, overrides),
registerModule('@stoplight/spectral-functions', functions, overrides),
registerModule('@stoplight/spectral-parsers', parsers, overrides),
registerModule('@stoplight/spectral-ref-resolver', refResolver, overrides),
registerModule('@stoplight/spectral-rulesets', rulesets, overrides),
registerModule('@stoplight/spectral-runtime', runtime, overrides),
registerModule(instanceId, '@stoplight/spectral-core', core, overrides),
registerModule(instanceId, '@stoplight/spectral-formats', formats, overrides),
registerModule(instanceId, '@stoplight/spectral-functions', functions, overrides),
registerModule(instanceId, '@stoplight/spectral-parsers', parsers, overrides),
registerModule(instanceId, '@stoplight/spectral-ref-resolver', refResolver, overrides),
registerModule(instanceId, '@stoplight/spectral-rulesets', rulesets, overrides),
registerModule(instanceId, '@stoplight/spectral-runtime', runtime, overrides),
]) as GlobalModules;

return {
name: '@stoplight-spectral/builtins',
resolveId(id) {
name: NAME,
resolveId(id): string | null {
if (id in modules) {
return id;
}

return null;
},
load(id) {
load(id): string | undefined {
if (id in modules) {
return modules[id] as string;
}
Expand Down

0 comments on commit b06903c

Please sign in to comment.