Skip to content

Commit

Permalink
fix(gatsby): show meaningful error message when engines try to bundle…
Browse files Browse the repository at this point in the history
… ts-node (#35762)

Co-authored-by: Lennart <lekoarts@gmail.com>
  • Loading branch information
pieh and LekoArts authored Jun 13, 2022
1 parent 9f3708f commit 123f202
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 21 deletions.
11 changes: 11 additions & 0 deletions packages/gatsby-cli/src/structured-errors/error-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ const errors = {
type: Type.WEBPACK,
level: Level.ERROR,
},
"98011": {
text: (context): string =>
`Rendering Engines attempted to use unsupported "${
context.package
}" package${
context.importedBy ? ` (imported by "${context.importedBy}")` : ``
}${context.advisory ? `\n\n${context.advisory}` : ``}`,
type: Type.WEBPACK,
level: Level.ERROR,
category: ErrorCategory.USER,
},
"98123": {
text: (context): string =>
`${context.stageLabel} failed\n\n${
Expand Down
99 changes: 78 additions & 21 deletions packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import * as path from "path"
import * as fs from "fs-extra"
import webpack from "webpack"
import webpack, { Module, NormalModule, Compilation } from "webpack"
import ConcatenatedModule from "webpack/lib/optimize/ConcatenatedModule"
import { printQueryEnginePlugins } from "./print-plugins"
import mod from "module"
import { WebpackLoggingPlugin } from "../../utils/webpack/plugins/webpack-logging"
Expand Down Expand Up @@ -77,6 +78,16 @@ export async function createGraphqlEngineBundle(
],
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: `babel-loader`,
options: {
presets: [`@babel/preset-typescript`],
},
},
},
{
oneOf: [
{
Expand All @@ -93,7 +104,7 @@ export async function createGraphqlEngineBundle(
},
{
// specific set of loaders for gatsby-node files - our babel transform that removes lifecycles not needed for engine -> relocator
test: /gatsby-node\.([cm]?js)$/,
test: /gatsby-node\.(cjs|mjs|js|ts)$/,
// it is recommended for Node builds to turn off AMD support
parser: { amd: false },
use: [
Expand All @@ -106,7 +117,7 @@ export async function createGraphqlEngineBundle(
{
// generic loader for all other cases than lmdb or gatsby-node - we don't do anything special other than using relocator on it
// For node binary relocations, include ".node" files as well here
test: /\.([cm]?js|node)$/,
test: /\.(cjs|mjs|js|ts|node)$/,
// it is recommended for Node builds to turn off AMD support
parser: { amd: false },
use: assetRelocatorUseEntry,
Expand All @@ -124,16 +135,6 @@ export async function createGraphqlEngineBundle(
},
},
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: `babel-loader`,
options: {
presets: [`@babel/preset-typescript`],
},
},
},
{
test: /\.txt/,
type: `asset/resource`,
Expand All @@ -150,6 +151,7 @@ export async function createGraphqlEngineBundle(
inquirer: false,
// only load one version of lmdb
lmdb: require.resolve(`lmdb`),
"ts-node": require.resolve(`./shims/ts-node`),
},
},
plugins: [
Expand All @@ -166,16 +168,71 @@ export async function createGraphqlEngineBundle(
})

return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
compiler.close(closeErr => {
if (err) {
return reject(err)
compiler.run((err, stats): void => {
function getResourcePath(
webpackModule?: Module | NormalModule | ConcatenatedModule | null
): string | undefined {
if (webpackModule && !(webpackModule instanceof ConcatenatedModule)) {
return (webpackModule as NormalModule).resource
}

if (webpackModule?.modules) {
// ConcatenatedModule is a collection of modules so we have to go deeper to actually get a path,
// at this point we won't know which one so we just grab first module here
const [firstSubModule] = webpackModule.modules
return getResourcePath(firstSubModule)
}
if (closeErr) {
return reject(closeErr)

return undefined
}

function iterateModules(
webpackModules: Set<Module>,
compilation: Compilation
): void {
for (const webpackModule of webpackModules) {
if (webpackModule instanceof ConcatenatedModule) {
iterateModules(
(webpackModule as ConcatenatedModule).modules,
compilation
)
} else {
const resourcePath = getResourcePath(webpackModule)
if (resourcePath?.includes(`ts-node`)) {
const importedBy = getResourcePath(
compilation.moduleGraph.getIssuer(webpackModule)
)
const structuredError = {
id: `98011`,
context: {
package: `ts-node`,
importedBy,
advisory: `Gatsby is supporting TypeScript natively (see https://gatsby.dev/typescript). "ts-node" might not be needed anymore at all, consider removing it.`,
},
}
throw structuredError
}
}
}
return resolve(stats?.compilation)
})
}

try {
if (stats?.compilation.modules) {
iterateModules(stats.compilation.modules, stats.compilation)
}

compiler.close(closeErr => {
if (err) {
return reject(err)
}
if (closeErr) {
return reject(closeErr)
}
return resolve(stats?.compilation)
})
} catch (e) {
reject(e)
}
})
})
}
12 changes: 12 additions & 0 deletions packages/gatsby/src/schema/graphql-engine/shims/ts-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function register() {
// no-op
//
// We are actually failing the build when `ts-node` exists in webpack's dependency graph
// because it's known to not work and cause failures.
//
// This shim for `ts-node` just skips trying to bundle the actual `ts-node`
// so webpack has less work to do during bundling.
//
// Using or not this shim, functionally doesn't make a difference - we will still
// fail the build with same actionable error anyway.
}
70 changes: 70 additions & 0 deletions packages/gatsby/src/schema/graphql-engine/standalone-regenerate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env node

/*
this is used for development purposes only
to be able to run `gatsby build` once to source data
and print schema and then just rebundle graphql-engine
with source file changes and test re-built engine quickly
Usage:
There need to be at least one successful `gatsby build`
before starting to use this script (warm up datastore,
generate "page-ssr" bundle). Once that's done you can
run following command in test site directory:
```shell
node node_modules/gatsby/dist/schema/graphql-engine/standalone-regenerate.js
```
*/

import { createGraphqlEngineBundle } from "./bundle-webpack"
import reporter from "gatsby-cli/lib/reporter"
import { loadConfigAndPlugins } from "../../utils/worker/child/load-config-and-plugins"
import * as fs from "fs-extra"
import { validateEngines } from "../../utils/validate-engines"

async function run(): Promise<void> {
// load config
console.log(`loading config and plugins`)
await loadConfigAndPlugins({
siteDirectory: process.cwd(),
})

try {
console.log(`clearing webpack cache\n\n`)
// get rid of cache if it exist
await fs.remove(process.cwd() + `/.cache/webpack/query-engine`)
} catch (e) {
// eslint-disable no-empty
}

// recompile
const buildActivityTimer = reporter.activityTimer(
`Building Rendering Engines`
)
try {
buildActivityTimer.start()
await createGraphqlEngineBundle(process.cwd(), reporter, true)
} catch (err) {
buildActivityTimer.panic(err)
} finally {
buildActivityTimer.end()
}

// validate
const validateEnginesActivity = reporter.activityTimer(
`Validating Rendering Engines`
)
validateEnginesActivity.start()
try {
await validateEngines(process.cwd())
} catch (error) {
validateEnginesActivity.panic({ id: `98001`, context: {}, error })
} finally {
validateEnginesActivity.end()
}

console.log(`DONE`)
}

run()

0 comments on commit 123f202

Please sign in to comment.