-
Notifications
You must be signed in to change notification settings - Fork 23
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
Cache busting config reloading by using virtual files #204
Changes from all commits
e289d8a
7549782
b820a13
15c4656
f273798
0764e67
f616edf
8a41bad
d13334b
e4ae818
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"vscode-apollo": patch | ||
--- | ||
|
||
Fix a bug where config file changes could not be picked up |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module.exports = { | ||
client: { | ||
service: { | ||
name: "localSchema", | ||
localSchemaFile: "./starwarsSchema.graphql", | ||
}, | ||
}, | ||
}; | ||
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
rover: | ||
profile: default |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// @ts-check | ||
const { pathToFileURL } = require("node:url"); | ||
|
||
/** @import { ResolveContext, ResolutionResult, LoadResult, ImportContext } from "./cache-busting-resolver.types" */ | ||
|
||
/** | ||
* @param {string} specifier | ||
* @returns {string} | ||
*/ | ||
function bustFileName(specifier) { | ||
const url = pathToFileURL(specifier); | ||
url.pathname = url.pathname + "." + Date.now() + ".js"; | ||
return url.toString(); | ||
} | ||
|
||
/** | ||
* | ||
* @param {string} specifier | ||
* @param {ResolveContext} context | ||
* @param {(specifier: string,context: ResolveContext) => Promise<ResolutionResult>} nextResolve | ||
* @returns {Promise<ResolutionResult>} | ||
*/ | ||
async function resolve(specifier, context, nextResolve) { | ||
if (context.importAttributes.as !== "cachebust") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was See, https://nodejs.org/api/module.html#hooks:~:text=v21.0.0%2C%20v20.10.0%2C%20v18.19.0 |
||
return nextResolve(specifier, context); | ||
} | ||
if (context.importAttributes.format) { | ||
// no need to resolve at all, we have all necessary information | ||
return { | ||
url: bustFileName(specifier), | ||
format: context.importAttributes.format, | ||
importAttributes: context.importAttributes, | ||
shortCircuit: true, | ||
}; | ||
} | ||
const result = await nextResolve(specifier, context); | ||
return { | ||
...result, | ||
url: bustFileName(result.url), | ||
importAttributes: context.importAttributes, | ||
}; | ||
} | ||
|
||
/** | ||
* | ||
* @param {string} url | ||
* @param {ImportContext} context | ||
* @param {(url: string, context: ImportContext) => Promise<LoadResult>} nextLoad | ||
* @returns {Promise<LoadResult>} | ||
*/ | ||
async function load(url, context, nextLoad) { | ||
if (context.importAttributes.as !== "cachebust") { | ||
return nextLoad(url, context); | ||
} | ||
return { | ||
format: context.format || "module", | ||
shortCircuit: true, | ||
source: context.importAttributes.contents, | ||
}; | ||
} | ||
|
||
module.exports = { | ||
resolve, | ||
load, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { pathToFileURL } from "node:url"; | ||
|
||
export type ImportAttributes = | ||
| { | ||
as: "cachebust"; | ||
contents: string; | ||
format?: Format; | ||
} | ||
| { as?: undefined }; | ||
|
||
type Format = | ||
| "builtin" | ||
| "commonjs" | ||
| "json" | ||
| "module" | ||
| "wasm" | ||
| null | ||
| undefined; | ||
|
||
export interface ResolveContext { | ||
conditions: string[]; | ||
importAttributes: ImportAttributes; | ||
parentURL?: string; | ||
} | ||
|
||
export interface ImportContext { | ||
conditions: string[]; | ||
importAttributes: ImportAttributes; | ||
format: Format; | ||
} | ||
|
||
export interface ResolutionResult { | ||
format: Format; | ||
importAttributes?: ImportAttributes; | ||
shortCircuit?: boolean; | ||
url: string; | ||
} | ||
|
||
export interface LoadResult { | ||
format: Format; | ||
shortCircuit?: boolean; | ||
source: string; | ||
} | ||
|
||
export {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,24 @@ | ||
import { Loader, defaultLoaders } from "cosmiconfig"; | ||
import { Loader } from "cosmiconfig"; | ||
import { dirname } from "node:path"; | ||
import { rm, writeFile } from "node:fs/promises"; | ||
import typescript from "typescript"; | ||
|
||
import { pathToFileURL } from "node:url"; | ||
import { register } from "node:module"; | ||
import { ImportAttributes } from "./cache-busting-resolver.types"; | ||
// implementation based on /~https://github.com/cosmiconfig/cosmiconfig/blob/a5a842547c13392ebb89a485b9e56d9f37e3cbd3/src/loaders.ts | ||
// Copyright (c) 2015 David Clark licensed MIT. Full license can be found here: | ||
// /~https://github.com/cosmiconfig/cosmiconfig/blob/a5a842547c13392ebb89a485b9e56d9f37e3cbd3/LICENSE | ||
|
||
if (process.env.JEST_WORKER_ID === undefined) { | ||
register( | ||
pathToFileURL(require.resolve("./config/cache-busting-resolver.js")), | ||
); | ||
} else { | ||
register(pathToFileURL(require.resolve("./cache-busting-resolver.js"))); | ||
} | ||
|
||
export const loadTs: Loader = async function loadTs(filepath, content) { | ||
try { | ||
return await load(filepath, content, ".vscode-extension-ignore.mjs", { | ||
return await load(filepath, content, "module", { | ||
module: typescript.ModuleKind.ES2022, | ||
}); | ||
} catch (error) { | ||
|
@@ -18,7 +27,7 @@ export const loadTs: Loader = async function loadTs(filepath, content) { | |
// [ERROR] ReferenceError: module is not defined in ES module scope | ||
error.message.includes("module is not defined") | ||
) { | ||
return await load(filepath, content, ".vscode-extension-ignore.cjs", { | ||
return await load(filepath, content, "commonjs", { | ||
module: typescript.ModuleKind.CommonJS, | ||
}); | ||
} else { | ||
|
@@ -30,36 +39,39 @@ export const loadTs: Loader = async function loadTs(filepath, content) { | |
async function load( | ||
filepath: string, | ||
content: string, | ||
extension: string, | ||
type: "module" | "commonjs", | ||
compilerOptions: Partial<import("typescript").CompilerOptions>, | ||
) { | ||
const compiledFilepath = `${filepath}${extension}`; | ||
let transpiledContent; | ||
|
||
try { | ||
try { | ||
const config = resolveTsConfig(dirname(filepath)) ?? {}; | ||
config.compilerOptions = { | ||
...config.compilerOptions, | ||
const config = resolveTsConfig(dirname(filepath)) ?? {}; | ||
config.compilerOptions = { | ||
...config.compilerOptions, | ||
|
||
moduleResolution: typescript.ModuleResolutionKind.Bundler, | ||
target: typescript.ScriptTarget.ES2022, | ||
noEmit: false, | ||
...compilerOptions, | ||
}; | ||
transpiledContent = typescript.transpileModule( | ||
content, | ||
config, | ||
).outputText; | ||
await writeFile(compiledFilepath, transpiledContent); | ||
} catch (error: any) { | ||
error.message = `TypeScript Error in ${filepath}:\n${error.message}`; | ||
throw error; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/return-await | ||
return await defaultLoaders[".js"](compiledFilepath, transpiledContent); | ||
} finally { | ||
await rm(compiledFilepath, { force: true }); | ||
moduleResolution: typescript.ModuleResolutionKind.Bundler, | ||
target: typescript.ScriptTarget.ES2022, | ||
noEmit: false, | ||
...compilerOptions, | ||
}; | ||
transpiledContent = typescript.transpileModule(content, config).outputText; | ||
} catch (error: any) { | ||
error.message = `TypeScript Error in ${filepath}:\n${error.message}`; | ||
throw error; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/return-await | ||
const imported = await import( | ||
filepath, | ||
//@ts-ignore | ||
{ | ||
with: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m trying to fix #225 and found out the issue is not only related to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As these are object keys, what happens if you pass this in as an object with both the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm :/ |
||
as: "cachebust", | ||
contents: transpiledContent, | ||
format: type, | ||
} satisfies ImportAttributes, | ||
} | ||
Comment on lines
+66
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be passed to our custom resolver, the file path will be largely ignored. |
||
); | ||
return imported.default; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
|
@@ -78,3 +90,17 @@ function resolveTsConfig(directory: string): any { | |
} | ||
return; | ||
} | ||
|
||
export const loadJs: Loader = async function loadJs(filepath, contents) { | ||
return ( | ||
await import( | ||
filepath, // @ts-ignore | ||
{ | ||
with: { | ||
as: "cachebust", | ||
contents, | ||
} satisfies ImportAttributes, | ||
} | ||
) | ||
).default; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -192,9 +192,6 @@ documents.onDidClose( | |
connection.onDidChangeWatchedFiles((params) => { | ||
const handledByProject = new Map<GraphQLProject, FileEvent[]>(); | ||
for (const { uri, type } of params.changes) { | ||
if (uri.match(/vscode-extension-ignore.[mc]?js$/)) { | ||
continue; | ||
} | ||
Comment on lines
-195
to
-197
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is no longer required since we don't create files on disk anymore |
||
const fsPath = URI.parse(uri).fsPath; | ||
const fileName = basename(fsPath); | ||
if ( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need one "TypeScript, but CommonJS" config for testing, and the rover config seemed like a better candidate for yaml anyways