mirror of
https://github.com/facebook/docusaurus.git
synced 2025-12-26 01:33:02 +00:00
Co-authored-by: Joshua Chen <sidachen2003@gmail.com> Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
290 lines
8.2 KiB
TypeScript
290 lines
8.2 KiB
TypeScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
import fs from 'fs-extra';
|
|
import path from 'path';
|
|
import tmp from 'tmp-promise';
|
|
import dedent from 'dedent';
|
|
import {loadFreshModule} from '../moduleUtils';
|
|
|
|
async function createTmpDir() {
|
|
return (
|
|
await tmp.dir({
|
|
prefix: 'jest-tmp-moduleUtils-tests',
|
|
})
|
|
).path;
|
|
}
|
|
|
|
async function moduleGraphHelpers() {
|
|
const dir = await createTmpDir();
|
|
|
|
async function fileHelper(name: string, initialContent?: string) {
|
|
const filePath = path.resolve(dir, name);
|
|
if (initialContent) {
|
|
await fs.outputFile(filePath, initialContent);
|
|
}
|
|
return {
|
|
filePath,
|
|
write: (content: string) => fs.outputFile(filePath, content),
|
|
load: () => loadFreshModule(filePath),
|
|
};
|
|
}
|
|
|
|
return {fileHelper};
|
|
}
|
|
|
|
async function loadFixtureModule(fixtureName: string) {
|
|
return loadFreshModule(
|
|
path.resolve(__dirname, '__fixtures__/moduleUtils', fixtureName),
|
|
);
|
|
}
|
|
|
|
describe('loadFreshModule', () => {
|
|
describe('can load CJS user module', () => {
|
|
async function testUserFixture(fixtureName: string) {
|
|
const userFixturePath = `user/${fixtureName}`;
|
|
const userModule = await loadFixtureModule(userFixturePath);
|
|
expect(userModule).toEqual({
|
|
birthYear: 1986,
|
|
firstName: 'Sebastien',
|
|
lastName: 'Lorber',
|
|
});
|
|
}
|
|
|
|
it('for .cjs.js', async () => {
|
|
await testUserFixture('user.cjs.js');
|
|
});
|
|
|
|
it('for .cjs.ts', async () => {
|
|
await testUserFixture('user.cjs.ts');
|
|
});
|
|
|
|
it('for .cjs', async () => {
|
|
await testUserFixture('user.cjs');
|
|
});
|
|
});
|
|
|
|
describe('can load ESM user module', () => {
|
|
async function testUserFixture(fixtureName: string) {
|
|
const userFixturePath = `user/${fixtureName}`;
|
|
const userModule = await loadFixtureModule(userFixturePath);
|
|
expect(userModule).toEqual({
|
|
birthYear: 1986,
|
|
firstName: 'Sebastien',
|
|
lastName: 'Lorber',
|
|
someNamedExport: 42,
|
|
});
|
|
}
|
|
|
|
it('for .esm.js', async () => {
|
|
await testUserFixture('user.esm.js');
|
|
});
|
|
|
|
it('for .esm.ts', async () => {
|
|
await testUserFixture('user.esm.ts');
|
|
});
|
|
|
|
it('for .mjs', async () => {
|
|
await testUserFixture('user.mjs');
|
|
});
|
|
});
|
|
|
|
describe('module graph', () => {
|
|
it('can load and reload fresh module graph', async () => {
|
|
const {fileHelper} = await moduleGraphHelpers();
|
|
|
|
const dependency1 = await fileHelper(
|
|
'dependency1.js',
|
|
/* language=js */
|
|
dedent`
|
|
export const dep1Export = "dep1 val1";
|
|
|
|
export default {dep1Val: "dep1 val2"}
|
|
`,
|
|
);
|
|
|
|
const dependency2 = await fileHelper(
|
|
'dependency2.ts',
|
|
/* language=ts */
|
|
dedent`
|
|
export default {dep2Val: "dep2 val"} satisfies {dep2Val: string}
|
|
`,
|
|
);
|
|
|
|
const entryFile = await fileHelper(
|
|
'entry.js',
|
|
/* language=js */
|
|
dedent`
|
|
import dependency1 from "./dependency1";
|
|
import dependency2 from "./dependency2";
|
|
|
|
export default {
|
|
someEntryValue: "entryVal",
|
|
dependency1,
|
|
dependency2
|
|
};
|
|
`,
|
|
);
|
|
|
|
// Should be able to read the initial module graph
|
|
await expect(entryFile.load()).resolves.toEqual({
|
|
someEntryValue: 'entryVal',
|
|
dependency1: {
|
|
dep1Export: 'dep1 val1',
|
|
dep1Val: 'dep1 val2',
|
|
},
|
|
dependency2: {
|
|
dep2Val: 'dep2 val',
|
|
},
|
|
});
|
|
await expect(dependency1.load()).resolves.toEqual({
|
|
dep1Export: 'dep1 val1',
|
|
dep1Val: 'dep1 val2',
|
|
});
|
|
await expect(dependency2.load()).resolves.toEqual({
|
|
dep2Val: 'dep2 val',
|
|
});
|
|
|
|
// Should be able to read the module graph again after updates
|
|
await dependency1.write(
|
|
/* language=js */
|
|
dedent`
|
|
export const dep1Export = "dep1 val1 updated";
|
|
|
|
export default {dep1Val: "dep1 val2 updated"}
|
|
`,
|
|
);
|
|
await expect(entryFile.load()).resolves.toEqual({
|
|
someEntryValue: 'entryVal',
|
|
dependency1: {
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
},
|
|
dependency2: {
|
|
dep2Val: 'dep2 val',
|
|
},
|
|
});
|
|
await expect(dependency1.load()).resolves.toEqual({
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
});
|
|
await expect(dependency2.load()).resolves.toEqual({
|
|
dep2Val: 'dep2 val',
|
|
});
|
|
|
|
// Should be able to read the module graph again after updates
|
|
await dependency2.write(
|
|
/* language=ts */
|
|
dedent`
|
|
export default {dep2Val: "dep2 val updated"} satisfies {dep2Val: string}
|
|
`,
|
|
);
|
|
await expect(entryFile.load()).resolves.toEqual({
|
|
someEntryValue: 'entryVal',
|
|
dependency1: {
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
},
|
|
dependency2: {
|
|
dep2Val: 'dep2 val updated',
|
|
},
|
|
});
|
|
await expect(dependency1.load()).resolves.toEqual({
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
});
|
|
await expect(dependency2.load()).resolves.toEqual({
|
|
dep2Val: 'dep2 val updated',
|
|
});
|
|
|
|
// Should be able to read the module graph again after updates
|
|
await entryFile.write(
|
|
/* language=js */
|
|
dedent`
|
|
import dependency1 from "./dependency1";
|
|
import dependency2 from "./dependency2";
|
|
|
|
export default {
|
|
someEntryValue: "entryVal updated",
|
|
dependency1,
|
|
dependency2,
|
|
newAttribute: "is there"
|
|
}
|
|
`,
|
|
);
|
|
await expect(entryFile.load()).resolves.toEqual({
|
|
someEntryValue: 'entryVal updated',
|
|
newAttribute: 'is there',
|
|
dependency1: {
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
},
|
|
dependency2: {
|
|
dep2Val: 'dep2 val updated',
|
|
},
|
|
});
|
|
await expect(dependency1.load()).resolves.toEqual({
|
|
dep1Export: 'dep1 val1 updated',
|
|
dep1Val: 'dep1 val2 updated',
|
|
});
|
|
await expect(dependency2.load()).resolves.toEqual({
|
|
dep2Val: 'dep2 val updated',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('invalid module path param', () => {
|
|
it('throws if module path does not exist', async () => {
|
|
await expect(() => loadFreshModule('/some/unknown/module/path.js'))
|
|
.rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
"Docusaurus could not load module at path "/some/unknown/module/path.js"
|
|
Cause: Cannot find module '/some/unknown/module/path.js' from 'packages/docusaurus-utils/src/moduleUtils.ts'"
|
|
`);
|
|
});
|
|
|
|
it('throws if module path is undefined', async () => {
|
|
await expect(() =>
|
|
// @ts-expect-error: undefined is invalid
|
|
loadFreshModule(undefined),
|
|
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
"Docusaurus could not load module at path "undefined"
|
|
Cause: Invalid module path of type undefined"
|
|
`);
|
|
});
|
|
|
|
it('throws if module path is null', async () => {
|
|
await expect(() =>
|
|
// @ts-expect-error: null is invalid
|
|
loadFreshModule(null),
|
|
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
"Docusaurus could not load module at path "null"
|
|
Cause: Invalid module path of type null"
|
|
`);
|
|
});
|
|
|
|
it('throws if module path is number', async () => {
|
|
await expect(() =>
|
|
// @ts-expect-error: number is invalid
|
|
loadFreshModule(42),
|
|
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
"Docusaurus could not load module at path "42"
|
|
Cause: Invalid module path of type 42"
|
|
`);
|
|
});
|
|
|
|
it('throws if module path is object', async () => {
|
|
await expect(() =>
|
|
// @ts-expect-error: object is invalid
|
|
loadFreshModule({}),
|
|
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
"Docusaurus could not load module at path "[object Object]"
|
|
Cause: Invalid module path of type [object Object]"
|
|
`);
|
|
});
|
|
});
|
|
});
|