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

chore: Properly typecheck gatsby #31519

Merged
merged 12 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions packages/gatsby/src/cache/cache-fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixes TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. throughout the file

Copy link
Contributor

Choose a reason for hiding this comment

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

TIL: about typing this as first param :O

Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this equal to DiskStore? (I'm also new to this in typescript 😂)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this was about having it implicitly:

TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

I didn't want to type everything so this is the best middleground for now

options = options || {}

this.options = {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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) {
Expand All @@ -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<void> {
DiskStore.prototype.reset = wrapCallback(async function (
this: any
): Promise<void> {
const readdir = promisify(fs.readdir)
const stat = promisify(fs.stat)
const unlink = promisify(fs.unlink)
Expand Down
6 changes: 4 additions & 2 deletions packages/gatsby/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ module.exports = async function build(program: IBuildArgs): Promise<void> {

telemetry.trackCli(`BUILD_START`)
signalExit(exitCode => {
telemetry.trackCli(`BUILD_END`, { exitCode })
telemetry.trackCli(`BUILD_END`, {
exitCode: exitCode as number | undefined,
})
})

const buildSpan = buildActivity.span
Expand Down Expand Up @@ -196,7 +198,7 @@ module.exports = async function build(program: IBuildArgs): Promise<void> {
{ parentSpan: buildSpan }
)
buildSSRBundleActivityProgress.start()
let pageRenderer: string
let pageRenderer = ``
Copy link
Contributor Author

Choose a reason for hiding this comment

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

"Used before initialized error" - setting a default here will also assign it string

let waitForCompilerCloseBuildHtml
try {
const result = await buildRenderer(program, Stage.BuildHTML, buildSpan)
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/commands/develop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ module.exports = async (program: IProgram): Promise<void> => {
null
)

let unlocks: Array<UnlockFn> = []
let unlocks: Array<UnlockFn | null> = []
pieh marked this conversation as resolved.
Show resolved Hide resolved
if (!isCI()) {
const statusUnlock = await createServiceLock(
program.directory,
Expand Down Expand Up @@ -416,7 +416,7 @@ module.exports = async (program: IProgram): Promise<void> => {
)

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 => {
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/commands/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ module.exports = async (program: IServeProgram): Promise<void> => {
let functions
try {
functions = JSON.parse(
fs.readFileSync(path.join(compiledFunctionsDir, `manifest.json`))
fs.readFileSync(path.join(compiledFunctionsDir, `manifest.json`), `utf-8`)
pieh marked this conversation as resolved.
Show resolved Hide resolved
)
} catch (e) {
// ignore
Expand All @@ -134,7 +134,7 @@ module.exports = async (program: IServeProgram): Promise<void> => {
`/api/*`,
multer().any(),
express.urlencoded({ extended: true }),
(req, res, next) => {
(req, _, next) => {
const cookies = req.headers.cookie

if (!cookies) {
Expand Down
144 changes: 64 additions & 80 deletions packages/gatsby/src/internal-plugins/functions/gatsby-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ 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 { getMatchPath, urlResolve } from "gatsby-core-utils"
import { CreateDevServerArgs, ParentSpanPluginArgs } from "gatsby"
import { internalActions } from "../../redux/actions"
import { IGatsbyFunction } from "../../redux/types"
import { reportWebpackWarnings } from "../../utils/webpack-error-utils"
import formatWebpackMessages from "react-dev-utils/formatWebpackMessages"
import dotenv from "dotenv"
Expand All @@ -17,23 +18,6 @@ import cookie from "cookie"

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
Expand Down Expand Up @@ -86,7 +70,10 @@ const createGlobArray = (siteDirectoryPath, plugins): Array<IGlobPattern> => {
return _.union(globs)
}

async function globAsync(pattern, options): Promise<Array<string>> {
async function globAsync(
pattern: string,
options: glob.IOptions = {}
): Promise<Array<string>> {
return await new Promise((resolve, reject) => {
glob(pattern, options, (err, files) => {
if (err) {
Expand All @@ -100,7 +87,6 @@ async function globAsync(pattern, options): Promise<Array<string>> {

const createWebpackConfig = async ({
siteDirectoryPath,
functionsDirectory,
store,
reporter,
}): Promise<webpack.Configuration> => {
Expand All @@ -118,43 +104,42 @@ 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<IFunctionData> = []
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<Array<IGatsbyFunction>> => {
const knownFunctions: Array<IGatsbyFunction> = []
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<IFunctionData>
const knownFunctions = _.unionBy(...allFunctions, func => func.functionRoute)

store.dispatch(internalActions.setFunctions(knownFunctions))

Expand All @@ -168,7 +153,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`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

defaultNodeEnv wasn't set

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I copy/pasted that from webpack.config.js and that didn't get changed

// 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
Expand All @@ -178,27 +163,30 @@ 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
)
}
}

const envObject = Object.keys(parsed).reduce((acc, key) => {
acc[key] = JSON.stringify(parsed[key])
return acc
}, {})
const envObject = Object.keys(parsed).reduce(
(acc, key) => {
acc[key] = JSON.stringify(parsed[key])
return acc
},
// Don't allow overwriting of NODE_ENV, PUBLIC_DIR as to not break gatsby things
{
LekoArts marked this conversation as resolved.
Show resolved Hide resolved
NODE_ENV: JSON.stringify(nodeEnv),
PUBLIC_DIR: JSON.stringify(`${siteDirectoryPath}/public`),
}
)

const varsFromProcessEnv = Object.keys(process.env).reduce((acc, key) => {
acc[key] = JSON.stringify(process.env[key])
return acc
}, {})

// Don't allow overwriting of NODE_ENV, PUBLIC_DIR as to not break gatsby things
envObject.NODE_ENV = JSON.stringify(nodeEnv)
envObject.PUBLIC_DIR = JSON.stringify(`${siteDirectoryPath}/public`)

const mergedEnvVars = Object.assign(envObject, varsFromProcessEnv)

const processEnvVars = Object.keys(mergedEnvVars).reduce(
Expand Down Expand Up @@ -227,7 +215,7 @@ const createWebpackConfig = async ({
? `functions-production`
: `functions-development`

const config = {
return {
entry: entries,
output: {
path: compiledFunctionsDir,
Expand Down Expand Up @@ -276,8 +264,6 @@ const createWebpackConfig = async ({
},
plugins: [new webpack.DefinePlugin(processEnvVars)],
}

return config
}

let isFirstBuild = true
Expand All @@ -292,13 +278,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,
Expand All @@ -312,10 +291,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<void>(async (resolve, reject) => {
const config = await createWebpackConfig({
siteDirectoryPath,
functionsDirectory,
store,
reporter,
})
Expand All @@ -334,11 +312,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
Expand Down Expand Up @@ -387,7 +365,6 @@ export async function onPreBootstrap({
compiler.close(async () => {
const config = await createWebpackConfig({
siteDirectoryPath,
functionsDirectory,
store,
reporter,
})
Expand All @@ -414,7 +391,7 @@ export async function onCreateDevServer({
`/api/*`,
multer().any(),
express.urlencoded({ extended: true }),
(req, res, next) => {
(req, _, next) => {
const cookies = req.headers.cookie

if (!cookies) {
Expand All @@ -433,7 +410,7 @@ export async function onCreateDevServer({

const {
functions,
}: { functions: Array<IFunctionData> } = store.getState()
}: { functions: Array<IGatsbyFunction> } = store.getState()

// Check first for exact matches.
let functionObj = functions.find(
Expand All @@ -445,7 +422,14 @@ export async function onCreateDevServer({
// We loop until we find the first match.
functions.some(f => {
let exp
const keys = []
interface IKey {
name: string
prefix: string
suffix: string
pattern: string
modifier: string
}
const keys: Array<IKey> = []
if (f.matchPath) {
exp = pathToRegexp(f.matchPath, keys)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby/src/redux/reducers/functions.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
Loading