From 640ce3654ed3f8f5bd232248beca8eb6e48ce3b3 Mon Sep 17 00:00:00 2001 From: Lennart Date: Fri, 28 May 2021 11:10:32 +0200 Subject: [PATCH] chore: Properly typecheck `gatsby` (#31519) Co-authored-by: Kyle Mathews --- .../src/bootstrap/schema-hot-reloader.ts | 2 +- packages/gatsby/src/cache/cache-fs.ts | 21 ++- packages/gatsby/src/cache/json-file-store.ts | 13 +- packages/gatsby/src/cache/wrap-callback.ts | 4 +- packages/gatsby/src/commands/build.ts | 6 +- packages/gatsby/src/commands/develop.ts | 22 ++- packages/gatsby/src/commands/serve.ts | 19 ++- .../internal-plugins/functions/gatsby-node.ts | 143 ++++++++---------- .../gatsby/src/redux/reducers/functions.ts | 2 +- packages/gatsby/src/redux/types.ts | 17 ++- .../gatsby/src/schema/types/pagination.ts | 4 +- packages/gatsby/src/utils/cache.ts | 3 +- packages/gatsby/src/utils/develop-proxy.ts | 4 +- .../limited-exports-page-templates.ts | 8 +- .../utils/gatsby-webpack-stats-extractor.ts | 4 +- packages/gatsby/src/utils/telemetry-server.ts | 2 +- packages/gatsby/src/utils/webpack-plugins.ts | 6 +- packages/gatsby/src/utils/webpack-utils.ts | 37 ++--- .../webpack/force-css-hmr-for-edge-cases.ts | 6 +- .../gatsby/src/utils/worker/render-html.ts | 8 +- packages/gatsby/src/utils/worker/worker.d.ts | 5 + packages/gatsby/tsconfig.json | 5 +- 22 files changed, 194 insertions(+), 147 deletions(-) create mode 100644 packages/gatsby/src/utils/worker/worker.d.ts diff --git a/packages/gatsby/src/bootstrap/schema-hot-reloader.ts b/packages/gatsby/src/bootstrap/schema-hot-reloader.ts index 2cdd1efcaeab1..3fc37dc809309 100644 --- a/packages/gatsby/src/bootstrap/schema-hot-reloader.ts +++ b/packages/gatsby/src/bootstrap/schema-hot-reloader.ts @@ -32,7 +32,7 @@ const maybeRebuildSchema = debounce(async (): Promise => { const activity = report.activityTimer(`rebuild schema`) activity.start() await rebuild({ parentSpan: activity }) - await updateStateAndRunQueries(false, { parentSpan: activity }) + await updateStateAndRunQueries(false, { parentSpan: activity.span }) activity.end() }, 1000) diff --git a/packages/gatsby/src/cache/cache-fs.ts b/packages/gatsby/src/cache/cache-fs.ts index b45b93edfc951..e7ea2ab869bf1 100644 --- a/packages/gatsby/src/cache/cache-fs.ts +++ b/packages/gatsby/src/cache/cache-fs.ts @@ -54,7 +54,7 @@ exports.create = function (args): typeof DiskStore { return new DiskStore(args && args.options ? args.options : args) } -function DiskStore(options): void { +function DiskStore(this: any, options): void { options = options || {} this.options = { @@ -91,7 +91,12 @@ function DiskStore(options): void { * @param {function} [cb] * @returns {Promise} */ -DiskStore.prototype.set = wrapCallback(async function (key, val, options) { +DiskStore.prototype.set = wrapCallback(async function ( + this: any, + key, + val, + options +) { key = key + `` const filePath = this._getFilePathByKey(key) @@ -128,7 +133,7 @@ DiskStore.prototype.set = wrapCallback(async function (key, val, options) { * @param {function} [cb] * @returns {Promise} */ -DiskStore.prototype.get = wrapCallback(async function (key) { +DiskStore.prototype.get = wrapCallback(async function (this: any, key) { key = key + `` const filePath = this._getFilePathByKey(key) @@ -172,7 +177,7 @@ DiskStore.prototype.get = wrapCallback(async function (key) { /** * delete entry from cache */ -DiskStore.prototype.del = wrapCallback(async function (key) { +DiskStore.prototype.del = wrapCallback(async function (this: any, key) { const filePath = this._getFilePathByKey(key) try { if (this.options.subdirs) { @@ -196,7 +201,9 @@ DiskStore.prototype.del = wrapCallback(async function (key) { /** * cleanup cache on disk -> delete all files from the cache */ -DiskStore.prototype.reset = wrapCallback(async function (): Promise { +DiskStore.prototype.reset = wrapCallback(async function ( + this: any +): Promise { const readdir = promisify(fs.readdir) const stat = promisify(fs.stat) const unlink = promisify(fs.unlink) @@ -265,10 +272,10 @@ function innerLock(resolve, reject, filePath): void { * unlocks a file path * @type {Function} * @param {string} filePath - * @returns {Promise} + * @returns {void} * @private */ -DiskStore.prototype._unlock = function _unlock(filePath): Promise { +DiskStore.prototype._unlock = function _unlock(filePath): void { globalGatsbyCacheLock.delete(filePath) } diff --git a/packages/gatsby/src/cache/json-file-store.ts b/packages/gatsby/src/cache/json-file-store.ts index dbbf026f12b10..4c6d8e8b67112 100644 --- a/packages/gatsby/src/cache/json-file-store.ts +++ b/packages/gatsby/src/cache/json-file-store.ts @@ -27,9 +27,14 @@ const promisify = require(`util`).promisify const fs = require(`fs`) const zlib = require(`zlib`) +interface IExternalBuffer { + index: number + buffer: Buffer +} + exports.write = async function (path, data, options): Promise { - const externalBuffers = [] - let dataString = JSON.stringify(data, function replacerFunction(k, value) { + const externalBuffers: Array = [] + let dataString = JSON.stringify(data, function replacerFunction(_k, value) { // Buffers searilize to {data: [...], type: "Buffer"} if ( value && @@ -103,10 +108,10 @@ exports.read = async function (path, options): Promise { ) } - const externalBuffers = [] + const externalBuffers: Array = [] let data try { - data = JSON.parse(dataString, function bufferReceiver(k, value) { + data = JSON.parse(dataString, function bufferReceiver(_k, value) { if (value && value.type === `Buffer` && value.data) { return Buffer.from(value.data) } else if ( diff --git a/packages/gatsby/src/cache/wrap-callback.ts b/packages/gatsby/src/cache/wrap-callback.ts index d3c892420728b..489e4956a87a2 100644 --- a/packages/gatsby/src/cache/wrap-callback.ts +++ b/packages/gatsby/src/cache/wrap-callback.ts @@ -37,8 +37,8 @@ module.exports = function wrapCallback( cb = args.pop() } - // eslint-disable-next-line @babel/no-invalid-this - const promise = fn.apply(this, args) + // @ts-ignore - unsure if fixing this introduces problems + const promise = fn.apply(this, args) // eslint-disable-line @babel/no-invalid-this if (typeof cb === `function`) { promise.then( diff --git a/packages/gatsby/src/commands/build.ts b/packages/gatsby/src/commands/build.ts index d36bec437d4e0..a8a19e5093af6 100644 --- a/packages/gatsby/src/commands/build.ts +++ b/packages/gatsby/src/commands/build.ts @@ -73,7 +73,9 @@ module.exports = async function build(program: IBuildArgs): Promise { telemetry.trackCli(`BUILD_START`) signalExit(exitCode => { - telemetry.trackCli(`BUILD_END`, { exitCode }) + telemetry.trackCli(`BUILD_END`, { + exitCode: exitCode as number | undefined, + }) }) const buildSpan = buildActivity.span @@ -196,7 +198,7 @@ module.exports = async function build(program: IBuildArgs): Promise { { parentSpan: buildSpan } ) buildSSRBundleActivityProgress.start() - let pageRenderer: string + let pageRenderer = `` let waitForCompilerCloseBuildHtml try { const result = await buildRenderer(program, Stage.BuildHTML, buildSpan) diff --git a/packages/gatsby/src/commands/develop.ts b/packages/gatsby/src/commands/develop.ts index 3cfdb7b630dbf..46a2ec8b30834 100644 --- a/packages/gatsby/src/commands/develop.ts +++ b/packages/gatsby/src/commands/develop.ts @@ -21,7 +21,7 @@ import { } from "gatsby-core-utils" import reporter from "gatsby-cli/lib/reporter" import { getSslCert } from "../utils/get-ssl-cert" -import { startDevelopProxy } from "../utils/develop-proxy" +import { IProxyControls, startDevelopProxy } from "../utils/develop-proxy" import { IProgram, IDebugInfo } from "./types" // Adapted from https://stackoverflow.com/a/16060619 @@ -295,7 +295,7 @@ module.exports = async (program: IProgram): Promise => { null ) - let unlocks: Array = [] + let unlocks: Array = [] if (!isCI()) { const statusUnlock = await createServiceLock( program.directory, @@ -416,7 +416,7 @@ module.exports = async (program: IProgram): Promise => { ) const files = [rootFile(`gatsby-config.js`), rootFile(`gatsby-node.js`)] - let watcher: chokidar.FSWatcher = null + let watcher: chokidar.FSWatcher if (!isCI()) { watcher = chokidar.watch(files).on(`change`, filePath => { @@ -498,6 +498,16 @@ module.exports = async (program: IProgram): Promise => { ) }) } + +interface IShutdownServicesOptions { + statusServer: https.Server | http.Server + developProcess: ControllableScript + proxy: IProxyControls + unlocks: Array + watcher: chokidar.FSWatcher + telemetryServerProcess: ControllableScript +} + function shutdownServices( { statusServer, @@ -506,7 +516,7 @@ function shutdownServices( unlocks, watcher, telemetryServerProcess, - }, + }: IShutdownServicesOptions, signal: NodeJS.Signals ): Promise { const services = [ @@ -518,7 +528,9 @@ function shutdownServices( ] unlocks.forEach(unlock => { - services.push(unlock()) + if (unlock) { + services.push(unlock()) + } }) return Promise.all(services) diff --git a/packages/gatsby/src/commands/serve.ts b/packages/gatsby/src/commands/serve.ts index e4a5b3cb530c0..5151acf9e5c20 100644 --- a/packages/gatsby/src/commands/serve.ts +++ b/packages/gatsby/src/commands/serve.ts @@ -10,7 +10,6 @@ import report from "gatsby-cli/lib/reporter" import multer from "multer" import pathToRegexp from "path-to-regexp" import cookie from "cookie" - import telemetry from "gatsby-telemetry" import { detectPortInUseAndPrompt } from "../utils/detect-port-in-use-and-prompt" @@ -18,12 +17,21 @@ import { getConfigFile } from "../bootstrap/get-config-file" import { preferDefault } from "../bootstrap/prefer-default" import { IProgram } from "./types" import { IPreparedUrls, prepareUrls } from "../utils/prepare-urls" +import { IGatsbyFunction } from "../redux/types" interface IMatchPath { path: string matchPath: string } +interface IPathToRegexpKey { + name: string | number + prefix: string + suffix: string + pattern: string + modifier: string +} + interface IServeProgram extends IProgram { prefixPaths: boolean } @@ -120,10 +128,10 @@ module.exports = async (program: IServeProgram): Promise => { `functions` ) - let functions + let functions: Array = [] try { functions = JSON.parse( - fs.readFileSync(path.join(compiledFunctionsDir, `manifest.json`)) + fs.readFileSync(path.join(compiledFunctionsDir, `manifest.json`), `utf-8`) ) } catch (e) { // ignore @@ -134,7 +142,7 @@ module.exports = async (program: IServeProgram): Promise => { `/api/*`, multer().any(), express.urlencoded({ extended: true }), - (req, res, next) => { + (req, _, next) => { const cookies = req.headers.cookie if (!cookies) { @@ -161,12 +169,13 @@ module.exports = async (program: IServeProgram): Promise => { // We loop until we find the first match. functions.some(f => { let exp - const keys = [] + const keys: Array = [] if (f.matchPath) { exp = pathToRegexp(f.matchPath, keys) } if (exp && exp.exec(pathFragment) !== null) { functionObj = f + // @ts-ignore - TS bug? https://stackoverflow.com/questions/50234481/typescript-2-8-3-type-must-have-a-symbol-iterator-method-that-returns-an-iterato const matches = [...pathFragment.match(exp)].slice(1) const newParams = {} matches.forEach( diff --git a/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts b/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts index 104f14f5d0c6d..9a3b84a3ccb8c 100644 --- a/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts +++ b/packages/gatsby/src/internal-plugins/functions/gatsby-node.ts @@ -5,35 +5,21 @@ import webpack from "webpack" import _ from "lodash" import multer from "multer" import * as express from "express" -import { urlResolve, getMatchPath } from "gatsby-core-utils" -import { ParentSpanPluginArgs, CreateDevServerArgs } from "gatsby" -import { internalActions } from "../../redux/actions" -import { reportWebpackWarnings } from "../../utils/webpack-error-utils" +import { getMatchPath, urlResolve } from "gatsby-core-utils" +import { CreateDevServerArgs, ParentSpanPluginArgs } from "gatsby" import formatWebpackMessages from "react-dev-utils/formatWebpackMessages" import dotenv from "dotenv" import chokidar from "chokidar" +// We use an ancient version of path-to-regexp as it has breaking changes to express v4 +// see: https://github.com/pillarjs/path-to-regexp/tree/77df63869075cfa5feda1988642080162c584427#compatibility-with-express--4x import pathToRegexp from "path-to-regexp" import cookie from "cookie" +import { reportWebpackWarnings } from "../../utils/webpack-error-utils" +import { internalActions } from "../../redux/actions" +import { IGatsbyFunction } from "../../redux/types" const isProductionEnv = process.env.gatsby_executing_command !== `develop` -interface IFunctionData { - /** The route in the browser to access the function **/ - functionRoute: string - /** The absolute path to the original function **/ - originalAbsoluteFilePath: string - /** The relative path to the original function **/ - originalRelativeFilePath: string - /** The relative path to the compiled function (always ends with .js) **/ - relativeCompiledFilePath: string - /** The absolute path to the compiled function (doesn't transfer across machines) **/ - absoluteCompiledFilePath: string - /** The matchPath regex created by path-to-regexp. Only created if the function is dynamic. **/ - matchPath: string - /** The plugin that owns this function route **/ - pluginName: string -} - interface IGlobPattern { /** The plugin that owns this namespace **/ pluginName: string @@ -43,15 +29,23 @@ interface IGlobPattern { globPattern: string } +interface IPathToRegexpKey { + name: string | number + prefix: string + suffix: string + pattern: string + modifier: string +} + // During development, we lazily compile functions only when they're requested. // Here we keep track of which functions have been requested so are "active" -const activeDevelopmentFunctions = new Set() +const activeDevelopmentFunctions = new Set() let activeEntries = {} async function ensureFunctionIsCompiled( - functionObj: IFunctionData, + functionObj: IGatsbyFunction, compiledFunctionsDir: string -): any { +): Promise { // stat the compiled function. If it's there, then return. let compiledFileExists = false try { @@ -125,7 +119,10 @@ const createGlobArray = (siteDirectoryPath, plugins): Array => { return _.union(globs) } -async function globAsync(pattern, options): Promise> { +async function globAsync( + pattern: string, + options: glob.IOptions = {} +): Promise> { return await new Promise((resolve, reject) => { glob(pattern, options, (err, files) => { if (err) { @@ -139,7 +136,6 @@ async function globAsync(pattern, options): Promise> { const createWebpackConfig = async ({ siteDirectoryPath, - functionsDirectory, store, reporter, }): Promise => { @@ -157,43 +153,43 @@ const createWebpackConfig = async ({ // Glob and return object with relative/absolute paths + which plugin // they belong to. const allFunctions = await Promise.all( - globs.map(async glob => { - const knownFunctions: Array = [] - const files = await globAsync(glob.globPattern) - files.map(file => { - const originalAbsoluteFilePath = file - const originalRelativeFilePath = path.relative(glob.rootPath, file) - - const { dir, name } = path.parse(originalRelativeFilePath) - // Ignore the original extension as all compiled functions now end with js. - const compiledFunctionName = path.join(dir, name + `.js`) - const compiledPath = path.join( - compiledFunctionsDir, - compiledFunctionName - ) - const finalName = urlResolve(dir, name === `index` ? `` : name) - - knownFunctions.push({ - functionRoute: finalName, - pluginName: glob.pluginName, - originalAbsoluteFilePath, - originalRelativeFilePath, - relativeCompiledFilePath: compiledFunctionName, - absoluteCompiledFilePath: compiledPath, - matchPath: getMatchPath(finalName), + globs.map( + async (glob): Promise> => { + const knownFunctions: Array = [] + const files = await globAsync(glob.globPattern) + files.map(file => { + const originalAbsoluteFilePath = file + const originalRelativeFilePath = path.relative(glob.rootPath, file) + + const { dir, name } = path.parse(originalRelativeFilePath) + // Ignore the original extension as all compiled functions now end with js. + const compiledFunctionName = path.join(dir, name + `.js`) + const compiledPath = path.join( + compiledFunctionsDir, + compiledFunctionName + ) + const finalName = urlResolve(dir, name === `index` ? `` : name) + + knownFunctions.push({ + functionRoute: finalName, + pluginName: glob.pluginName, + originalAbsoluteFilePath, + originalRelativeFilePath, + relativeCompiledFilePath: compiledFunctionName, + absoluteCompiledFilePath: compiledPath, + matchPath: getMatchPath(finalName), + }) }) - }) - return knownFunctions - }) + return knownFunctions + } + ) ) // Combine functions by the route name so that functions in the default // functions directory can override the plugin's implementations. - const knownFunctions = _.unionBy( - ...allFunctions, - func => func.functionRoute - ) as Array + // @ts-ignore - Seems like a TS bug: https://github.com/microsoft/TypeScript/issues/28010#issuecomment-713484584 + const knownFunctions = _.unionBy(...allFunctions, func => func.functionRoute) store.dispatch(internalActions.setFunctions(knownFunctions)) @@ -207,7 +203,7 @@ const createWebpackConfig = async ({ // Logic is shared with webpack.config.js // node env should be DEVELOPMENT | PRODUCTION as these are commonly used in node land - const nodeEnv = process.env.NODE_ENV || `${defaultNodeEnv}` + const nodeEnv = process.env.NODE_ENV || `development` // config env is dependent on the env that it's run, this can be anything from staging-production // this allows you to set use different .env environments or conditions in gatsby files const configEnv = process.env.GATSBY_ACTIVE_ENV || nodeEnv @@ -217,7 +213,7 @@ const createWebpackConfig = async ({ parsed = dotenv.parse(fs.readFileSync(envFile, { encoding: `utf8` })) } catch (err) { if (err.code !== `ENOENT`) { - report.error( + reporter.error( `There was a problem processing the .env file (${envFile})`, err ) @@ -227,12 +223,12 @@ const createWebpackConfig = async ({ const envObject = Object.keys(parsed).reduce((acc, key) => { acc[key] = JSON.stringify(parsed[key]) return acc - }, {}) + }, {} as Record) const varsFromProcessEnv = Object.keys(process.env).reduce((acc, key) => { acc[key] = JSON.stringify(process.env[key]) return acc - }, {}) + }, {} as Record) // Don't allow overwriting of NODE_ENV, PUBLIC_DIR as to not break gatsby things envObject.NODE_ENV = JSON.stringify(nodeEnv) @@ -271,7 +267,7 @@ const createWebpackConfig = async ({ ? `functions-production` : `functions-development` - const config = { + return { entry: entries, output: { path: compiledFunctionsDir, @@ -320,8 +316,6 @@ const createWebpackConfig = async ({ }, plugins: [new webpack.DefinePlugin(processEnvVars)], } - - return config } let isFirstBuild = true @@ -336,13 +330,6 @@ export async function onPreBootstrap({ program: { directory: siteDirectoryPath }, } = store.getState() - const functionsDirectoryPath = path.join(siteDirectoryPath, `src/api`) - - const functionsDirectory = path.resolve( - siteDirectoryPath, - functionsDirectoryPath as string - ) - reporter.verbose(`Attaching functions to development server`) const compiledFunctionsDir = path.join( siteDirectoryPath, @@ -357,10 +344,9 @@ export async function onPreBootstrap({ // We do this ungainly thing as we need to make accessible // the resolve/reject functions to our shared callback function // eslint-disable-next-line - await new Promise(async (resolve, reject) => { + await new Promise(async (resolve, reject) => { const config = await createWebpackConfig({ siteDirectoryPath, - functionsDirectory, store, reporter, }) @@ -379,11 +365,11 @@ export async function onPreBootstrap({ if (isProductionEnv) { if (errors.length > 0) return reject(stats.compilation.errors) } else { - const formated = formatWebpackMessages({ + const formatted = formatWebpackMessages({ errors: rawMessages.errors.map(e => e.message), warnings: [], }) - reporter.error(formated.errors) + reporter.error(formatted.errors) } // Log success in dev @@ -437,7 +423,6 @@ export async function onPreBootstrap({ compiler.close(async () => { const config = await createWebpackConfig({ siteDirectoryPath, - functionsDirectory, store, reporter, }) @@ -474,7 +459,7 @@ export async function onCreateDevServer({ `/api/*`, multer().any(), express.urlencoded({ extended: true }), - (req, res, next) => { + (req, _, next) => { const cookies = req.headers.cookie if (!cookies) { @@ -493,7 +478,7 @@ export async function onCreateDevServer({ const { functions, - }: { functions: Array } = store.getState() + }: { functions: Array } = store.getState() // Check first for exact matches. let functionObj = functions.find( @@ -505,7 +490,7 @@ export async function onCreateDevServer({ // We loop until we find the first match. functions.some(f => { let exp - const keys = [] + const keys: Array = [] if (f.matchPath) { exp = pathToRegexp(f.matchPath, keys) } diff --git a/packages/gatsby/src/redux/reducers/functions.ts b/packages/gatsby/src/redux/reducers/functions.ts index 0f0117903a690..93c396bce09ee 100644 --- a/packages/gatsby/src/redux/reducers/functions.ts +++ b/packages/gatsby/src/redux/reducers/functions.ts @@ -1,7 +1,7 @@ import { IGatsbyState, ISetSiteFunctions } from "../types" export const functionsReducer = ( - state: IGatsbyState["functions"] = new Map(), + state: IGatsbyState["functions"] = [], action: ISetSiteFunctions ): IGatsbyState["functions"] => { switch (action.type) { diff --git a/packages/gatsby/src/redux/types.ts b/packages/gatsby/src/redux/types.ts index ce4c28846f295..6ba336fc23989 100644 --- a/packages/gatsby/src/redux/types.ts +++ b/packages/gatsby/src/redux/types.ts @@ -40,7 +40,20 @@ export interface IGatsbyPage { } export interface IGatsbyFunction { - path: string + /** The route in the browser to access the function **/ + functionRoute: string + /** The absolute path to the original function **/ + originalAbsoluteFilePath: string + /** The relative path to the original function **/ + originalRelativeFilePath: string + /** The relative path to the compiled function (always ends with .js) **/ + relativeCompiledFilePath: string + /** The absolute path to the compiled function (doesn't transfer across machines) **/ + absoluteCompiledFilePath: string + /** The matchPath regex created by path-to-regexp. Only created if the function is dynamic. **/ + matchPath: string | undefined + /** The plugin that owns this function route **/ + pluginName: string } export interface IGatsbyConfig { @@ -228,7 +241,7 @@ export interface IGatsbyState { pluginFilepath: SystemPath }> config: IGatsbyConfig - functions: Map + functions: Array pages: Map schema: GraphQLSchema definitions: Map diff --git a/packages/gatsby/src/schema/types/pagination.ts b/packages/gatsby/src/schema/types/pagination.ts index 3c4cca5ec2fd8..9d30d4ff17b30 100644 --- a/packages/gatsby/src/schema/types/pagination.ts +++ b/packages/gatsby/src/schema/types/pagination.ts @@ -3,7 +3,7 @@ import { ObjectTypeComposer, InputTypeComposer, InterfaceTypeComposer, - ComposeFieldConfigMap, + ObjectTypeComposerFieldConfigMapDefinition, } from "graphql-compose" import { getFieldsEnum } from "./sort" import { addDerivedType } from "./derived-types" @@ -52,7 +52,7 @@ const createPagination = ({ }: { schemaComposer: SchemaComposer typeComposer: ObjectTypeComposer | InterfaceTypeComposer - fields: ComposeFieldConfigMap + fields: ObjectTypeComposerFieldConfigMapDefinition typeName: string }): ObjectTypeComposer => { const paginationTypeComposer: ObjectTypeComposer = schemaComposer.getOrCreateOTC( diff --git a/packages/gatsby/src/utils/cache.ts b/packages/gatsby/src/utils/cache.ts index 207d32b8f2c97..2fc59f13f7e8b 100644 --- a/packages/gatsby/src/utils/cache.ts +++ b/packages/gatsby/src/utils/cache.ts @@ -5,7 +5,7 @@ import manager, { MultiCache, } from "cache-manager" import fs from "fs-extra" -import fsStore from "../cache/cache-fs" +import * as fsStore from "../cache/cache-fs" import path from "path" const MAX_CACHE_SIZE = 250 @@ -22,6 +22,7 @@ export default class GatsbyCache { public directory: string public cache?: MultiCache + // @ts-ignore - set & get types are missing from fsStore? constructor({ name = `db`, store = fsStore }: ICacheProperties = {}) { this.name = name this.store = store diff --git a/packages/gatsby/src/utils/develop-proxy.ts b/packages/gatsby/src/utils/develop-proxy.ts index 9f46bf8fe78c4..9a1c4e986bceb 100644 --- a/packages/gatsby/src/utils/develop-proxy.ts +++ b/packages/gatsby/src/utils/develop-proxy.ts @@ -8,10 +8,10 @@ import st from "st" import restartingScreen from "./restarting-screen" import { IProgram } from "../commands/types" -interface IProxyControls { +export interface IProxyControls { serveRestartingScreen: () => void serveSite: () => void - server: any + server: https.Server | http.Server } const noop = (): void => {} diff --git a/packages/gatsby/src/utils/eslint-rules/limited-exports-page-templates.ts b/packages/gatsby/src/utils/eslint-rules/limited-exports-page-templates.ts index 9284bb7645e07..6bfd10af28346 100644 --- a/packages/gatsby/src/utils/eslint-rules/limited-exports-page-templates.ts +++ b/packages/gatsby/src/utils/eslint-rules/limited-exports-page-templates.ts @@ -10,6 +10,7 @@ import { TemplateLiteral, VariableDeclarator, ObjectPattern, + AssignmentProperty, } from "estree" import { store } from "../../redux" import { isPageTemplate } from "../eslint-rules-helpers" @@ -127,11 +128,14 @@ const limitedExports: Rule.RuleModule = { // Search for "graphql" in a const { graphql, Link } = require('gatsby') const graphqlTagSpecifier = ((requiredFromGatsby as VariableDeclarator) .id as ObjectPattern)?.properties.find( - el => (el.key as Identifier).name === DEFAULT_GRAPHQL_TAG_NAME + el => + ((el as AssignmentProperty).key as Identifier).name === + DEFAULT_GRAPHQL_TAG_NAME ) if (graphqlTagSpecifier) { - graphqlTagName = (graphqlTagSpecifier.value as Identifier).name + graphqlTagName = ((graphqlTagSpecifier as AssignmentProperty) + .value as Identifier).name } } diff --git a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts index 038d2566a2db4..0c950066a16db 100644 --- a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts +++ b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.ts @@ -23,7 +23,7 @@ export class GatsbyWebpackStatsExtractor { .filter( f => f.slice(-4) !== `.map` && - f.slice(0, chunkGroup.name.length) === chunkGroup.name + f.slice(0, chunkGroup.name?.length) === chunkGroup.name ) .map(filename => `/${filename}`) @@ -37,7 +37,7 @@ export class GatsbyWebpackStatsExtractor { childAssets[chunkGroup.name] = {} } - const childFiles = [] + const childFiles: Array = [] for (const childChunkGroup of childChunkGroups) { for (const chunk of childChunkGroup.chunks) { childFiles.push(...chunk.files) diff --git a/packages/gatsby/src/utils/telemetry-server.ts b/packages/gatsby/src/utils/telemetry-server.ts index f5aab4baa2d11..65bdda74dcead 100644 --- a/packages/gatsby/src/utils/telemetry-server.ts +++ b/packages/gatsby/src/utils/telemetry-server.ts @@ -29,7 +29,7 @@ const app = express() app.use(cors()) // Overview over all possible routes at / -app.get(`/`, (req, res) => { +app.get(`/`, (_, res) => { res.set(`Content-Type`, `text/html`) res.send( `
    diff --git a/packages/gatsby/src/utils/webpack-plugins.ts b/packages/gatsby/src/utils/webpack-plugins.ts index add1ea7725ad0..c62f06530ac4b 100644 --- a/packages/gatsby/src/utils/webpack-plugins.ts +++ b/packages/gatsby/src/utils/webpack-plugins.ts @@ -1,10 +1,12 @@ -import webpack, { Plugin } from "webpack" +import webpack, { WebpackPluginInstance } from "webpack" const plugin = ( name: string, optimize?: boolean, deprecationReason?: string -): ((...args: any) => Plugin) => (...args: any): Plugin => { +): ((...args: any) => WebpackPluginInstance) => ( + ...args: any +): WebpackPluginInstance => { if (deprecationReason) { // TODO add proper deprecation function to reporter console.warn(`[deprecated]: ${deprecationReason}`) diff --git a/packages/gatsby/src/utils/webpack-utils.ts b/packages/gatsby/src/utils/webpack-utils.ts index 921b7c93dcd0c..e11bca243b6fd 100644 --- a/packages/gatsby/src/utils/webpack-utils.ts +++ b/packages/gatsby/src/utils/webpack-utils.ts @@ -1,6 +1,7 @@ import * as path from "path" -import { Loader, RuleSetRule, Plugin } from "webpack" +import { RuleSetRule, WebpackPluginInstance } from "webpack" import { GraphQLSchema } from "graphql" +import { Plugin as PostCSSPlugin } from "postcss" import autoprefixer from "autoprefixer" import flexbugs from "postcss-flexbugs-fixes" import TerserPlugin from "terser-webpack-plugin" @@ -21,6 +22,7 @@ import { builtinPlugins } from "./webpack-plugins" import { IProgram, Stage } from "../commands/types" import { eslintConfig, eslintRequiredConfig } from "./eslint-config" +type Loader = string | { loader: string; options?: { [name: string]: any } } type LoaderResolver> = (options?: T) => Loader type LoaderOptions = Record @@ -33,7 +35,7 @@ type ContextualRuleFactory> = RuleFactory & { external?: RuleFactory } -type PluginFactory = (...args: any) => Plugin +type PluginFactory = (...args: any) => WebpackPluginInstance type BuiltinPlugins = typeof builtinPlugins @@ -86,9 +88,7 @@ interface ILoaderUtils { postcss: LoaderResolver<{ browsers?: Array overrideBrowserslist?: Array - plugins?: - | Array> - | ((loader: Loader) => Array>) + plugins?: Array | ((loader: Loader) => Array) }> file: LoaderResolver @@ -100,8 +100,8 @@ interface ILoaderUtils { dependencies: LoaderResolver miniCssExtract: LoaderResolver - imports: LoaderResolver - exports: LoaderResolver + imports?: LoaderResolver + exports?: LoaderResolver } interface IModuleThatUseGatsby { @@ -310,7 +310,7 @@ export const createWebpackUtils = ( // eslint-disable-next-line @typescript-eslint/explicit-function-return-type postcssOptions: (loaderContext: any) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any - let postCSSPlugins: Array> = [] + let postCSSPlugins: Array = [] if (plugins) { postCSSPlugins = typeof plugins === `function` ? plugins(loaderContext) : plugins @@ -319,9 +319,9 @@ export const createWebpackUtils = ( const autoprefixerPlugin = autoprefixer({ overrideBrowserslist, flexbox: `no-2009`, - ...((postCSSPlugins.find( + ...(((postCSSPlugins.find( plugin => plugin.postcssPlugin === `autoprefixer` - ) as autoprefixer.Autoprefixer)?.options ?? {}), + ) as unknown) as autoprefixer.ExportedAPI)?.options ?? {}), }) postCSSPlugins.unshift(autoprefixerPlugin) @@ -587,7 +587,7 @@ export const createWebpackUtils = ( !isSSR && loaders.miniCssExtract(restOptions), loaders.css({ ...restOptions, importLoaders: 1 }), loaders.postcss({ browsers }), - ].filter(Boolean) + ].filter(Boolean) as RuleSetRule["use"] return { use, @@ -641,7 +641,9 @@ export const createWebpackUtils = ( plugins.minifyJs = ({ terserOptions, ...options - }: { terserOptions?: TerserPlugin.TerserPluginOptions } = {}): Plugin => + }: { + terserOptions?: TerserPlugin.TerserPluginOptions + } = {}): WebpackPluginInstance => new TerserPlugin({ exclude: /\.min\.js/, terserOptions: { @@ -735,7 +737,7 @@ export const createWebpackUtils = ( ...options, }) - plugins.fastRefresh = ({ modulesThatUseGatsby }): Plugin => { + plugins.fastRefresh = ({ modulesThatUseGatsby }): WebpackPluginInstance => { const regExpToHack = /node_modules/ regExpToHack.test = (modulePath: string): boolean => { // when it's not coming from node_modules we treat it as a source file. @@ -762,12 +764,13 @@ export const createWebpackUtils = ( }) } - plugins.extractText = (options: any): Plugin => + plugins.extractText = (options: any): WebpackPluginInstance => new MiniCssExtractPlugin({ ...options, }) - plugins.moment = (): Plugin => plugins.ignore(/^\.\/locale$/, /moment$/) + plugins.moment = (): WebpackPluginInstance => + plugins.ignore(/^\.\/locale$/, /moment$/) plugins.extractStats = (): GatsbyWebpackStatsExtractor => new GatsbyWebpackStatsExtractor() @@ -778,7 +781,7 @@ export const createWebpackUtils = ( plugins.virtualModules = (): GatsbyWebpackVirtualModules => new GatsbyWebpackVirtualModules() - plugins.eslint = (schema: GraphQLSchema): Plugin => { + plugins.eslint = (schema: GraphQLSchema): WebpackPluginInstance => { const options = { extensions: [`js`, `jsx`], exclude: [ @@ -792,7 +795,7 @@ export const createWebpackUtils = ( return new ESLintPlugin(options) } - plugins.eslintRequired = (): Plugin => { + plugins.eslintRequired = (): WebpackPluginInstance => { const options = { extensions: [`js`, `jsx`], exclude: [ diff --git a/packages/gatsby/src/utils/webpack/force-css-hmr-for-edge-cases.ts b/packages/gatsby/src/utils/webpack/force-css-hmr-for-edge-cases.ts index 7f6b3257080c3..97ff440442499 100644 --- a/packages/gatsby/src/utils/webpack/force-css-hmr-for-edge-cases.ts +++ b/packages/gatsby/src/utils/webpack/force-css-hmr-for-edge-cases.ts @@ -14,8 +14,8 @@ import { Compiler, Module } from "webpack" */ export class ForceCssHMRForEdgeCases { private name: string - private originalBlankCssHash: string - private blankCssKey: string + private originalBlankCssHash: string | undefined + private blankCssKey: string | undefined private hackCounter = 0 private previouslySeenCss: Set = new Set() @@ -63,6 +63,7 @@ export class ForceCssHMRForEdgeCases { if ( !this.originalBlankCssHash && + // @ts-ignore - exists on NormalModule but not Module module.rawRequest === `./blank.css` ) { this.blankCssKey = key @@ -70,6 +71,7 @@ export class ForceCssHMRForEdgeCases { records.chunkModuleHashes[this.blankCssKey] } + // @ts-ignore - exists on NormalModule but not Module const isUsingMiniCssExtract = module.loaders?.find(loader => loader?.loader?.includes(`mini-css-extract-plugin`) ) diff --git a/packages/gatsby/src/utils/worker/render-html.ts b/packages/gatsby/src/utils/worker/render-html.ts index 6ceb59f0af362..fb9962c5b439c 100644 --- a/packages/gatsby/src/utils/worker/render-html.ts +++ b/packages/gatsby/src/utils/worker/render-html.ts @@ -8,10 +8,6 @@ import { IRenderHtmlResult } from "../../commands/build-html" // we want to force posix-style joins, so Windows doesn't produce backslashes for urls const { join } = path.posix -declare global { - let unsafeBuiltinUsage: Array | undefined -} - /** * Used to track if renderHTMLProd / renderHTMLDev are called within same "session" (from same renderHTMLQueue call). * As long as sessionId remains the same we can rely on memoized/cached resources for templates, css file content for inlining and static query results. @@ -45,7 +41,7 @@ function clearCaches(): void { const getStaticQueryPath = (hash: string): string => join(`page-data`, `sq`, `d`, `${hash}.json`) -const getStaticQueryResult = async (hash: string): any => { +const getStaticQueryResult = async (hash: string): Promise => { const staticQueryPath = getStaticQueryPath(hash) const absoluteStaticQueryPath = join(process.cwd(), `public`, staticQueryPath) const staticQueryRaw = await fs.readFile(absoluteStaticQueryPath) @@ -64,7 +60,7 @@ async function readPageData( return JSON.parse(rawPageData) } -async function readWebpackStats(publicDir: string): any { +async function readWebpackStats(publicDir: string): Promise { const filePath = join(publicDir, `webpack.stats.json`) const rawPageData = await fs.readFile(filePath, `utf-8`) diff --git a/packages/gatsby/src/utils/worker/worker.d.ts b/packages/gatsby/src/utils/worker/worker.d.ts new file mode 100644 index 0000000000000..3601f3cfec52f --- /dev/null +++ b/packages/gatsby/src/utils/worker/worker.d.ts @@ -0,0 +1,5 @@ +declare module NodeJS { + interface Global { + unsafeBuiltinUsage: Array | undefined + } +} diff --git a/packages/gatsby/tsconfig.json b/packages/gatsby/tsconfig.json index 3ad9a20cee4b1..a6faaa7652b8d 100644 --- a/packages/gatsby/tsconfig.json +++ b/packages/gatsby/tsconfig.json @@ -1,5 +1,6 @@ { "extends": "../../tsconfig.json", - // This is for typegen purposes only. For now index.d.ts is manually-created, but gatsby/internal is auto-generated - "include": ["./src/internal.ts"] + // For now index.d.ts is manually-created, but gatsby/internal is auto-generated + "include": ["src/**/*.ts"], + "exclude": ["**/__tests__/**/*"] }