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

feat: support "type": "module" #485

Closed
wants to merge 13 commits into from
79 changes: 63 additions & 16 deletions packages/cli/src/__tests__/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,22 @@ test("all the build types", async () => {
)
).toMatchInlineSnapshot(`
"\\"use strict\\";
// this file might look strange and you might be wondering what it's for
// it's lets you import your source files by importing this entrypoint
// as you would import it if it was built with preconstruct build
// this file is slightly different to some others though
// it has a require hook which compiles your code with Babel
// this means that you don't have to set up @babel/register or anything like that
// but you can still require this module and it'll be compiled

// this bit of code imports the require hook and registers it
let unregister = require(\\"RELATIVE_PATH_TO_PRECONSTRUCT_HOOK\\").___internalHook(typeof __dirname === 'undefined' ? undefined : __dirname, \\"..\\", \\"..\\");

// this re-exports the source file
module.exports = require(\\"../src/index.js\\");

unregister();
"
// this file might look strange and you might be wondering what it's for
// it's lets you import your source files by importing this entrypoint
// as you would import it if it was built with preconstruct build
// this file is slightly different to some others though
// it has a require hook which compiles your code with Babel
// this means that you don't have to set up @babel/register or anything like that
// but you can still require this module and it'll be compiled
// this bit of code imports the require hook and registers it
let unregister = require(\\"RELATIVE_PATH_TO_PRECONSTRUCT_HOOK\\").___internalHook(typeof __dirname === 'undefined' ? undefined : __dirname, \\"..\\", \\"..\\");
// this re-exports the source file
module.exports = require(\\"../src/index.js\\");
unregister();
"
`);

let shouldBeCjsThingsToSource = [
Expand Down Expand Up @@ -395,3 +395,50 @@ test("flow and .d.ts", async () => {
export const x = "hello";
`);
});

test("esm", async () => {
let tmpPath = realFs.realpathSync.native(
await testdir({
"package.json": JSON.stringify({
name: "all-the-build-types",
type: "module",
main: "dist/all-the-build-types.esm.js",
browser: {
"./dist/all-the-build-types.esm.js":
"./dist/all-the-build-types.browser.esm.js",
},
}),
"src/index.js": js`
export default "some cool thing";
`,
})
);

await dev(tmpPath);

let distPath = path.join(tmpPath, "dist");
expect(await fs.readdir(distPath)).toEqual([
"all-the-build-types.browser.esm.js",
"all-the-build-types.esm.js",
]);

expect(
await fs.readFile(
path.join(distPath, "all-the-build-types.esm.js"),
"utf-8"
)
).toMatchInlineSnapshot(`"export default \\"some cool thing\\";"`);

let shouldBeEsmThingsToSource = [
"all-the-build-types.esm.js",
"all-the-build-types.browser.esm.js",
];

await Promise.all(
shouldBeEsmThingsToSource.map(async (filename) => {
expect(await fs.realpath(path.join(distPath, filename))).toBe(
path.join(tmpPath, "src/index.js")
);
})
);
});
106 changes: 106 additions & 0 deletions packages/cli/src/__tests__/fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -982,3 +982,109 @@ test("project level exports field config", async () => {

`);
});

test("fix incorrect main field for module type", async () => {
const tmpPath = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
type: "module",
main: "dist/pkg-a.cjs.js",
}),
"src/index.js": "",
});
await fix(tmpPath);
expect(await getFiles(tmpPath, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"type": "module",
"main": "dist/pkg-a.esm.js"
}

`);
});

test("remove module field when module type", async () => {
const tmpPath = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
type: "module",
main: "dist/pkg-a.esm.js",
module: "dist/pkg-a.esm.js",
}),
"src/index.js": "",
});
await fix(tmpPath);
expect(await getFiles(tmpPath, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"type": "module",
"main": "dist/pkg-a.esm.js"
}

`);
});

test("fix incorrect main field for commonjs type", async () => {
const tmpPath = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
main: "dist/pkg-a.esm.js",
}),
"src/index.js": "",
});
await fix(tmpPath);
expect(await getFiles(tmpPath, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"main": "dist/pkg-a.cjs.js"
}

`);
});

test("module type with three entrypoints (no main, add main and module)", async () => {
const tmpPath = await testdir({
"package.json": JSON.stringify({
name: "something",
type: "module",
preconstruct: {
entrypoints: ["index.js", "one.js", "two.js"],
},
}),
"src/index.js": "",
"src/one.js": "",
"src/two.js": "",
"one/package.json": JSON.stringify({}),
"two/package.json": JSON.stringify({}),
Comment on lines +1060 to +1061
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably a safe bet to assume that if a tool understands type: 'module' then it also understands the package.json#exports field. So assuming that, we could stop generating nested package.json directories for entrypoints and rely on package.json#exports entirely for entrypoints.

});
await fix(tmpPath);
expect(await getFiles(tmpPath, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ one/package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"main": "dist/something-one.esm.js"
}

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "something",
"type": "module",
"preconstruct": {
"entrypoints": [
"index.js",
"one.js",
"two.js"
]
},
"main": "dist/something.esm.js"
}

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ two/package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"main": "dist/something-two.esm.js"
}

`);
});
114 changes: 114 additions & 0 deletions packages/cli/src/__tests__/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,117 @@ test("three entrypoints, no main, add main and fix browser", async () => {

`);
});

test("fix incorrect main field for module type", async () => {
const dir = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
type: "module",
main: "dist/pkg-a.cjs.js",
}),
"src/index.js": "",
});
confirms.writeMainField.mockReturnValue(Promise.resolve(true));

await init(dir);

expect(await getFiles(dir, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"type": "module",
"main": "dist/pkg-a.esm.js"
}

`);
});

test("remove module field when module type", async () => {
const dir = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
type: "module",
main: "dist/pkg-a.esm.js",
module: "dist/pkg-a.esm.js",
}),
"src/index.js": "",
});
confirms.writeMainField.mockReturnValue(Promise.resolve(true));
confirms.writeModuleField.mockReturnValue(Promise.resolve(true));

await init(dir);

expect(await getFiles(dir, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"type": "module",
"main": "dist/pkg-a.esm.js"
}

`);
});

test("fix incorrect main field for commonjs type", async () => {
const dir = await testdir({
"package.json": JSON.stringify({
name: "pkg-a",
main: "dist/pkg-a.esm.js",
}),
"src/index.js": "",
});
confirms.writeMainField.mockReturnValue(Promise.resolve(true));

await init(dir);

expect(await getFiles(dir, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "pkg-a",
"main": "dist/pkg-a.cjs.js"
}

`);
});

test("module type with three entrypoints (no main, add main and module)", async () => {
const dir = await testdir({
...basicThreeEntrypoints,
"package.json": JSON.stringify({
...JSON.parse(basicThreeEntrypoints["package.json"]),
type: "module",
}),
});

confirms.writeMainField.mockReturnValue(Promise.resolve(true));
confirms.writeModuleField.mockReturnValue(Promise.resolve(true));

await init(dir);

expect(await getFiles(dir, ["**/package.json"])).toMatchInlineSnapshot(`
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ one/package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"main": "dist/something-one.esm.js"
}

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"name": "something",
"preconstruct": {
"entrypoints": [
"index.js",
"one.js",
"two.js"
]
},
"type": "module",
"main": "dist/something.esm.js"
}

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ two/package.json ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
{
"main": "dist/something-two.esm.js"
}

`);
});