Skip to content

Commit 123f202

Browse files
piehLekoArts
andauthored
fix(gatsby): show meaningful error message when engines try to bundle ts-node (#35762)
Co-authored-by: Lennart <[email protected]>
1 parent 9f3708f commit 123f202

File tree

4 files changed

+171
-21
lines changed

4 files changed

+171
-21
lines changed

packages/gatsby-cli/src/structured-errors/error-map.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ const errors = {
6464
type: Type.WEBPACK,
6565
level: Level.ERROR,
6666
},
67+
"98011": {
68+
text: (context): string =>
69+
`Rendering Engines attempted to use unsupported "${
70+
context.package
71+
}" package${
72+
context.importedBy ? ` (imported by "${context.importedBy}")` : ``
73+
}${context.advisory ? `\n\n${context.advisory}` : ``}`,
74+
type: Type.WEBPACK,
75+
level: Level.ERROR,
76+
category: ErrorCategory.USER,
77+
},
6778
"98123": {
6879
text: (context): string =>
6980
`${context.stageLabel} failed\n\n${

packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
import * as path from "path"
44
import * as fs from "fs-extra"
5-
import webpack from "webpack"
5+
import webpack, { Module, NormalModule, Compilation } from "webpack"
6+
import ConcatenatedModule from "webpack/lib/optimize/ConcatenatedModule"
67
import { printQueryEnginePlugins } from "./print-plugins"
78
import mod from "module"
89
import { WebpackLoggingPlugin } from "../../utils/webpack/plugins/webpack-logging"
@@ -77,6 +78,16 @@ export async function createGraphqlEngineBundle(
7778
],
7879
module: {
7980
rules: [
81+
{
82+
test: /\.ts$/,
83+
exclude: /node_modules/,
84+
use: {
85+
loader: `babel-loader`,
86+
options: {
87+
presets: [`@babel/preset-typescript`],
88+
},
89+
},
90+
},
8091
{
8192
oneOf: [
8293
{
@@ -93,7 +104,7 @@ export async function createGraphqlEngineBundle(
93104
},
94105
{
95106
// specific set of loaders for gatsby-node files - our babel transform that removes lifecycles not needed for engine -> relocator
96-
test: /gatsby-node\.([cm]?js)$/,
107+
test: /gatsby-node\.(cjs|mjs|js|ts)$/,
97108
// it is recommended for Node builds to turn off AMD support
98109
parser: { amd: false },
99110
use: [
@@ -106,7 +117,7 @@ export async function createGraphqlEngineBundle(
106117
{
107118
// generic loader for all other cases than lmdb or gatsby-node - we don't do anything special other than using relocator on it
108119
// For node binary relocations, include ".node" files as well here
109-
test: /\.([cm]?js|node)$/,
120+
test: /\.(cjs|mjs|js|ts|node)$/,
110121
// it is recommended for Node builds to turn off AMD support
111122
parser: { amd: false },
112123
use: assetRelocatorUseEntry,
@@ -124,16 +135,6 @@ export async function createGraphqlEngineBundle(
124135
},
125136
},
126137
},
127-
{
128-
test: /\.ts$/,
129-
exclude: /node_modules/,
130-
use: {
131-
loader: `babel-loader`,
132-
options: {
133-
presets: [`@babel/preset-typescript`],
134-
},
135-
},
136-
},
137138
{
138139
test: /\.txt/,
139140
type: `asset/resource`,
@@ -150,6 +151,7 @@ export async function createGraphqlEngineBundle(
150151
inquirer: false,
151152
// only load one version of lmdb
152153
lmdb: require.resolve(`lmdb`),
154+
"ts-node": require.resolve(`./shims/ts-node`),
153155
},
154156
},
155157
plugins: [
@@ -166,16 +168,71 @@ export async function createGraphqlEngineBundle(
166168
})
167169

168170
return new Promise((resolve, reject) => {
169-
compiler.run((err, stats) => {
170-
compiler.close(closeErr => {
171-
if (err) {
172-
return reject(err)
171+
compiler.run((err, stats): void => {
172+
function getResourcePath(
173+
webpackModule?: Module | NormalModule | ConcatenatedModule | null
174+
): string | undefined {
175+
if (webpackModule && !(webpackModule instanceof ConcatenatedModule)) {
176+
return (webpackModule as NormalModule).resource
177+
}
178+
179+
if (webpackModule?.modules) {
180+
// ConcatenatedModule is a collection of modules so we have to go deeper to actually get a path,
181+
// at this point we won't know which one so we just grab first module here
182+
const [firstSubModule] = webpackModule.modules
183+
return getResourcePath(firstSubModule)
173184
}
174-
if (closeErr) {
175-
return reject(closeErr)
185+
186+
return undefined
187+
}
188+
189+
function iterateModules(
190+
webpackModules: Set<Module>,
191+
compilation: Compilation
192+
): void {
193+
for (const webpackModule of webpackModules) {
194+
if (webpackModule instanceof ConcatenatedModule) {
195+
iterateModules(
196+
(webpackModule as ConcatenatedModule).modules,
197+
compilation
198+
)
199+
} else {
200+
const resourcePath = getResourcePath(webpackModule)
201+
if (resourcePath?.includes(`ts-node`)) {
202+
const importedBy = getResourcePath(
203+
compilation.moduleGraph.getIssuer(webpackModule)
204+
)
205+
const structuredError = {
206+
id: `98011`,
207+
context: {
208+
package: `ts-node`,
209+
importedBy,
210+
advisory: `Gatsby is supporting TypeScript natively (see https://gatsby.dev/typescript). "ts-node" might not be needed anymore at all, consider removing it.`,
211+
},
212+
}
213+
throw structuredError
214+
}
215+
}
176216
}
177-
return resolve(stats?.compilation)
178-
})
217+
}
218+
219+
try {
220+
if (stats?.compilation.modules) {
221+
iterateModules(stats.compilation.modules, stats.compilation)
222+
}
223+
224+
compiler.close(closeErr => {
225+
if (err) {
226+
return reject(err)
227+
}
228+
if (closeErr) {
229+
return reject(closeErr)
230+
}
231+
return resolve(stats?.compilation)
232+
})
233+
} catch (e) {
234+
reject(e)
235+
}
179236
})
180237
})
181238
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function register() {
2+
// no-op
3+
//
4+
// We are actually failing the build when `ts-node` exists in webpack's dependency graph
5+
// because it's known to not work and cause failures.
6+
//
7+
// This shim for `ts-node` just skips trying to bundle the actual `ts-node`
8+
// so webpack has less work to do during bundling.
9+
//
10+
// Using or not this shim, functionally doesn't make a difference - we will still
11+
// fail the build with same actionable error anyway.
12+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/env node
2+
3+
/*
4+
this is used for development purposes only
5+
to be able to run `gatsby build` once to source data
6+
and print schema and then just rebundle graphql-engine
7+
with source file changes and test re-built engine quickly
8+
9+
Usage:
10+
There need to be at least one successful `gatsby build`
11+
before starting to use this script (warm up datastore,
12+
generate "page-ssr" bundle). Once that's done you can
13+
run following command in test site directory:
14+
15+
```shell
16+
node node_modules/gatsby/dist/schema/graphql-engine/standalone-regenerate.js
17+
```
18+
*/
19+
20+
import { createGraphqlEngineBundle } from "./bundle-webpack"
21+
import reporter from "gatsby-cli/lib/reporter"
22+
import { loadConfigAndPlugins } from "../../utils/worker/child/load-config-and-plugins"
23+
import * as fs from "fs-extra"
24+
import { validateEngines } from "../../utils/validate-engines"
25+
26+
async function run(): Promise<void> {
27+
// load config
28+
console.log(`loading config and plugins`)
29+
await loadConfigAndPlugins({
30+
siteDirectory: process.cwd(),
31+
})
32+
33+
try {
34+
console.log(`clearing webpack cache\n\n`)
35+
// get rid of cache if it exist
36+
await fs.remove(process.cwd() + `/.cache/webpack/query-engine`)
37+
} catch (e) {
38+
// eslint-disable no-empty
39+
}
40+
41+
// recompile
42+
const buildActivityTimer = reporter.activityTimer(
43+
`Building Rendering Engines`
44+
)
45+
try {
46+
buildActivityTimer.start()
47+
await createGraphqlEngineBundle(process.cwd(), reporter, true)
48+
} catch (err) {
49+
buildActivityTimer.panic(err)
50+
} finally {
51+
buildActivityTimer.end()
52+
}
53+
54+
// validate
55+
const validateEnginesActivity = reporter.activityTimer(
56+
`Validating Rendering Engines`
57+
)
58+
validateEnginesActivity.start()
59+
try {
60+
await validateEngines(process.cwd())
61+
} catch (error) {
62+
validateEnginesActivity.panic({ id: `98001`, context: {}, error })
63+
} finally {
64+
validateEnginesActivity.end()
65+
}
66+
67+
console.log(`DONE`)
68+
}
69+
70+
run()

0 commit comments

Comments
 (0)