Skip to content

Commit

Permalink
Use esbuild to build server-side code (#7809)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Cousens <413395+dcousens@users.noreply.github.com>
  • Loading branch information
emmatown and dcousens committed Sep 9, 2022
1 parent dca5c49 commit 309786f
Show file tree
Hide file tree
Showing 32 changed files with 558 additions and 478 deletions.
5 changes: 5 additions & 0 deletions .changeset/giant-avocados-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': patch
---

Changed platform compilation to use [esbuild](https://esbuild.github.io/), previously used next.js
27 changes: 6 additions & 21 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ module.exports = {
node: true,
jest: true,
},
plugins: [
'react',
'react-hooks',
'jest',
'import',
'@typescript-eslint',
'@preconstruct/format-js-tag',
],
plugins: ['react', 'react-hooks', 'jest', 'import', '@typescript-eslint'],
settings: {
react: {
version: 'detect',
Expand Down Expand Up @@ -103,31 +96,23 @@ module.exports = {
},
},
],
'@preconstruct/format-js-tag/format': 'error',
},
extends: ['plugin:jest/recommended'],

// Disable some rules for (code blocks within) Markdown docs
overrides: [
{
files: ['**/*.md'],
files: ['**/*.{ts,tsx}'],
rules: {
'no-unused-vars': 'off',
// TypeScript already checks for the following things and they conflict with TypeScript
'import/no-unresolved': 'off',
'no-undef': 'off',
},
},
{
files: ['packages/fields/src/**/*.{js,ts,tsx}'],
rules: {
'import/no-commonjs': 'error',
},
},
{
files: ['**/*.{ts,tsx}'],
files: ['packages/core/src/scripts/tests/fixtures/**/*.{ts,tsx}'],
rules: {
// TypeScript already checks for the following things and they conflict with TypeScript
'import/no-unresolved': 'off',
'no-undef': 'off',
'import/no-extraneous-dependencies': 'off',
},
},
],
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@
"@jest/test-sequencer": "^29.0.0",
"@manypkg/cli": "^0.19.1",
"@preconstruct/cli": "2.2.1",
"@preconstruct/eslint-plugin-format-js-tag": "^0.2.0",
"@testing-library/jest-dom": "^5.15.0",
"@types/babel__core": "^7.1.16",
"@types/jest": "^29.0.0",
"@types/node-fetch": "^2.5.12",
"@typescript-eslint/eslint-plugin": "^5.7.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"main": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-require-source.cjs.js",
"module": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-require-source.esm.js"
"main": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-load-config.cjs.js",
"module": "dist/keystone-6-core-___internal-do-not-use-will-break-in-patch-load-config.esm.js"
}
10 changes: 2 additions & 8 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"@aws-sdk/client-s3": "^3.83.0",
"@aws-sdk/lib-storage": "^3.83.0",
"@aws-sdk/s3-request-presigner": "^3.83.0",
"@babel/core": "^7.16.0",
"@babel/runtime": "^7.16.3",
"@emotion/hash": "^0.9.0",
"@emotion/weak-memoize": "^0.3.0",
Expand Down Expand Up @@ -65,11 +64,9 @@
"@types/express": "^4.17.13",
"@types/fs-extra": "^9.0.13",
"@types/inflection": "^1.13.0",
"@types/node-fetch": "^2.5.12",
"@types/pluralize": "^0.0.29",
"@types/prompts": "^2.0.14",
"@types/react": "^18.0.9",
"@types/source-map-support": "^0.5.4",
"@types/supertest": "^2.0.11",
"@types/uid-safe": "^2.1.2",
"@types/uuid": "^8.3.1",
Expand All @@ -89,6 +86,7 @@
"date-fns": "^2.26.0",
"decimal.js": "10.4.0",
"dumb-passwords": "^0.2.1",
"esbuild": "^0.15.5",
"execa": "^5.1.1",
"express": "^4.17.1",
"fast-deep-equal": "^3.1.3",
Expand All @@ -105,16 +103,13 @@
"meow": "^9.0.0",
"micro": "^9.3.4",
"next": "^12.2.4",
"node-fetch": "^2.6.7",
"p-limit": "^2.3.0",
"pirates": "4.0.4",
"pluralize": "^8.0.0",
"prisma": "4.3.1",
"prompts": "^2.4.2",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"resolve": "^1.20.0",
"source-map-support": "^0.5.20",
"supertest": "^6.1.6",
"ts-toolbelt": "^9.6.0",
"uid-safe": "^2.1.5",
Expand All @@ -125,7 +120,6 @@
"fast-glob": "^3.2.7",
"fixturez": "^1.1.0",
"mime": "^3.0.0",
"outdent": "^0.8.0",
"string-argv": "^0.3.1",
"strip-ansi": "^6.0.1"
},
Expand All @@ -134,7 +128,7 @@
"index.ts",
"system.ts",
"next.ts",
"___internal-do-not-use-will-break-in-patch/{node-api,next-graphql,require-source}.ts",
"___internal-do-not-use-will-break-in-patch/{node-api,next-graphql,load-config}.ts",
"___internal-do-not-use-will-break-in-patch/admin-ui/pages/*/index.tsx",
"___internal-do-not-use-will-break-in-patch/admin-ui/{next-config.ts,id-field-view.tsx}",
"artifacts.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { loadConfigOnce as loadConfig } from '../lib/config/loadConfig';

This file was deleted.

44 changes: 44 additions & 0 deletions packages/core/src/lib/config/loadConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import esbuild, { BuildOptions } from 'esbuild';
import { KeystoneConfig } from '../../types';
import { getBuiltConfigPath } from '../../scripts/utils';
import { initConfig } from './initConfig';

export function getEsbuildConfig(cwd: string): BuildOptions {
return {
entryPoints: ['./keystone'],
absWorkingDir: cwd,
bundle: true,
outfile: '.keystone/config.js',
format: 'cjs',
plugins: [
{
name: 'external-node_modules',
setup(build) {
build.onResolve(
{
// this regex is intended to be the opposite of /^\.\.?(?:\/|$)/
// so it matches anything that isn't a relative import
// so this means that we're only going to bundle relative imports
// we can't use a negative lookahead/lookbehind because this regex is executed
// by Go's regex package which doesn't support them
// this regex could have less duplication with nested groups but this is probably easier to read
filter: /(?:^[^.])|(?:^\.[^/.])|(?:^\.\.[^/])/,
},
args => {
return { external: true, path: args.path };
}
);
},
},
],
};
}

export function loadBuiltConfig(cwd: string): KeystoneConfig {
return initConfig(require(getBuiltConfigPath(cwd)).default);
}

export async function loadConfigOnce(cwd: string): Promise<KeystoneConfig> {
await esbuild.build(getEsbuildConfig(cwd));
return loadBuiltConfig(cwd);
}
62 changes: 0 additions & 62 deletions packages/core/src/lib/config/requireSource.ts

This file was deleted.

64 changes: 5 additions & 59 deletions packages/core/src/scripts/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,11 @@
import Path from 'path';
import fs from 'fs-extra';
import { AdminFileToWrite } from '../../types';
import { buildAdminUI, generateAdminUI } from '../../admin-ui/system';
import { createSystem } from '../../lib/createSystem';
import { initConfig } from '../../lib/config/initConfig';
import { requireSource } from '../../lib/config/requireSource';
import { generateNodeModulesArtifacts, validateCommittedArtifacts } from '../../artifacts';
import { getAdminPath, getConfigPath } from '../utils';
import { serializePathForImport } from '../../admin-ui/utils/serializePathForImport';
import { writeAdminFile } from '../../admin-ui/system/generateAdminUI';

const reexportKeystoneConfig = async (cwd: string, isDisabled?: boolean) => {
const projectAdminPath = getAdminPath(cwd);
const configPath = getConfigPath(cwd);
if (isDisabled) {
// Nuke any existing files in our target directory
await fs.remove(projectAdminPath);
}

// We re-export the Keystone config file into the Admin UI project folder
// so that when we run the build step, we will end up with a compiled version
// of the configuration file in the .next/ directory. Even if we're not building
// an Admin UI, we still need to run the `build()` function so that this config
// file is correctly compiled.
const pkgDir = Path.dirname(require.resolve('@keystone-6/core/package.json'));
const p = serializePathForImport(
Path.relative(Path.join(projectAdminPath, 'pages', 'api'), configPath)
);
const files: AdminFileToWrite[] = [
{
mode: 'write',
src: `export { default as config } from ${p};
export default function (req, res) { return res.status(500) }`,
outputPath: Path.join('pages', 'api', '__keystone_api_build.js'),
},
];
if (isDisabled) {
// These are the basic files required to have a valid Next.js project. If the
// Admin UI is disabled then we need to do this ourselves here.
files.push(
{
mode: 'copy' as const,
inputPath: Path.join(pkgDir, 'static', 'next.config.js'),
outputPath: 'next.config.js',
},
{
mode: 'copy' as const,
inputPath: Path.join(pkgDir, 'static', 'tsconfig.json'),
outputPath: 'tsconfig.json',
}
);
}
await Promise.all(files.map(file => writeAdminFile(file, projectAdminPath)));
};
import { getAdminPath } from '../utils';
import { loadConfigOnce } from '../../lib/config/loadConfig';

export async function build(cwd: string) {
const config = initConfig(requireSource(getConfigPath(cwd)).default);
const config = await loadConfigOnce(cwd);

const { graphQLSchema, adminMeta } = createSystem(config);

Expand All @@ -71,11 +21,7 @@ export async function build(cwd: string) {
} else {
console.log('✨ Generating Admin UI code');
await generateAdminUI(config, graphQLSchema, adminMeta, getAdminPath(cwd), false);
console.log('✨ Building Admin UI');
await buildAdminUI(getAdminPath(cwd));
}

console.log('✨ Generating Keystone config code');
await reexportKeystoneConfig(cwd, config.ui?.isDisabled);

console.log('✨ Building Admin UI');
await buildAdminUI(getAdminPath(cwd));
}
6 changes: 2 additions & 4 deletions packages/core/src/scripts/postinstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import {
generateNodeModulesArtifacts,
validateCommittedArtifacts,
} from '../artifacts';
import { requireSource } from '../lib/config/requireSource';
import { initConfig } from '../lib/config/initConfig';
import { getConfigPath } from './utils';
import { loadConfigOnce } from '../lib/config/loadConfig';

// The postinstall step serves two purposes:

Expand Down Expand Up @@ -40,7 +38,7 @@ import { getConfigPath } from './utils';
// * only generated with generateNodeAPI option

export async function postinstall(cwd: string, shouldFix: boolean) {
const config = initConfig(requireSource(getConfigPath(cwd)).default);
const config = await loadConfigOnce(cwd);

const { graphQLSchema } = createSystem(config);

Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/scripts/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import execa from 'execa';
import { createSystem } from '../lib/createSystem';
import { generateNodeModulesArtifacts, validateCommittedArtifacts } from '../artifacts';
import { requireSource } from '../lib/config/requireSource';
import { initConfig } from '../lib/config/initConfig';
import { ExitError, getConfigPath } from './utils';
import { loadConfigOnce } from '../lib/config/loadConfig';
import { ExitError } from './utils';

export async function prisma(cwd: string, args: string[]) {
const config = initConfig(requireSource(getConfigPath(cwd)).default);
const config = await loadConfigOnce(cwd);

const { graphQLSchema } = createSystem(config);

Expand Down
Loading

0 comments on commit 309786f

Please sign in to comment.