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

Updates vfs to be more future safe when running on node + 2021 dts files for playground #2802

Merged
merged 11 commits into from
Jul 25, 2023
5 changes: 5 additions & 0 deletions packages/typescript-vfs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### 1.5

- Makes `createDefaultMapFromNodeModules` pull all the `.d.ts` files from the node_modules folder, not just the `.d.ts` files which were known ahead of time.
- Updates the known .d.s files to include ones from TypeScript 5.1 beta.

### 1.3

- Adds a JS file into the npm tarball for using with a vanilla script tag, which sets `global.tsvfs` with exported function.
Expand Down
102 changes: 80 additions & 22 deletions packages/typescript-vfs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,13 @@ export function createVirtualTypeScriptEnvironment(
}
}

// TODO: This could be replaced by grabbing: https://github.com/microsoft/TypeScript/blob/main/src/lib/libs.json
// and then using that to generate the list of files from the server, but it is not included in the npm package
jakebailey marked this conversation as resolved.
Show resolved Hide resolved

/**
* Grab the list of lib files for a particular target, will return a bit more than necessary (by including
* the dom) but that's OK
* the dom) but that's OK, we're really working with the constraint that you can't get a list of files
* when running in a browser.
*
* @param target The compiler settings target baseline
* @param ts A copy of the TypeScript module
Expand All @@ -102,12 +106,18 @@ export const knownLibFilesForCompilerOptions = (compilerOptions: CompilerOptions
const target = compilerOptions.target || ts.ScriptTarget.ES5
const lib = compilerOptions.lib || []

// Note that this will include files which can't be found for particular versions of TS
// TODO: Replace this with some sort of API call if https://github.com/microsoft/TypeScript/pull/54011
// or similar is merged.
const files = [
"lib.d.ts",
"lib.decorators.d.ts",
"lib.decorators.legacy.d.ts",
"lib.dom.d.ts",
"lib.dom.iterable.d.ts",
"lib.webworker.d.ts",
"lib.webworker.importscripts.d.ts",
"lib.webworker.iterable.d.ts",
"lib.scripthost.d.ts",
"lib.es5.d.ts",
"lib.es6.d.ts",
Expand All @@ -125,6 +135,7 @@ export const knownLibFilesForCompilerOptions = (compilerOptions: CompilerOptions
"lib.es2016.d.ts",
"lib.es2016.full.d.ts",
"lib.es2017.d.ts",
"lib.es2017.date.d.ts",
"lib.es2017.full.d.ts",
"lib.es2017.intl.d.ts",
"lib.es2017.object.d.ts",
Expand All @@ -141,28 +152,51 @@ export const knownLibFilesForCompilerOptions = (compilerOptions: CompilerOptions
"lib.es2019.array.d.ts",
"lib.es2019.d.ts",
"lib.es2019.full.d.ts",
"lib.es2019.intl.d.ts",
"lib.es2019.object.d.ts",
"lib.es2019.string.d.ts",
"lib.es2019.symbol.d.ts",
"lib.es2020.bigint.d.ts",
"lib.es2020.d.ts",
"lib.es2020.date.d.ts",
"lib.es2020.full.d.ts",
"lib.es2020.string.d.ts",
"lib.es2020.symbol.wellknown.d.ts",
"lib.es2020.bigint.d.ts",
"lib.es2020.intl.d.ts",
"lib.es2020.number.d.ts",
"lib.es2020.promise.d.ts",
"lib.es2020.sharedmemory.d.ts",
"lib.es2020.intl.d.ts",
"lib.es2020.string.d.ts",
"lib.es2020.symbol.wellknown.d.ts",
"lib.es2021.d.ts",
"lib.es2021.full.d.ts",
"lib.es2021.intl.d.ts",
"lib.es2021.promise.d.ts",
"lib.es2021.string.d.ts",
"lib.es2021.weakref.d.ts",
"lib.es2022.array.d.ts",
"lib.es2022.d.ts",
"lib.es2022.error.d.ts",
"lib.es2022.full.d.ts",
"lib.es2022.intl.d.ts",
"lib.es2022.object.d.ts",
"lib.es2022.regexp.d.ts",
"lib.es2022.sharedmemory.d.ts",
"lib.es2022.string.d.ts",
"lib.es2023.array.d.ts",
"lib.es2023.collection.d.ts",
"lib.es2023.d.ts",
"lib.es2023.full.d.ts",
"lib.esnext.array.d.ts",
"lib.esnext.asynciterable.d.ts",
"lib.esnext.bigint.d.ts",
"lib.esnext.d.ts",
"lib.esnext.decorators.d.ts",
"lib.esnext.disposable.d.ts",
"lib.esnext.full.d.ts",
"lib.esnext.intl.d.ts",
"lib.esnext.promise.d.ts",
"lib.esnext.string.d.ts",
"lib.esnext.weakref.d.ts",
"lib.esnext.symbol.d.ts",
"lib.esnext.weakref.d.ts"
]

const targetToCut = ts.ScriptTarget[target]
Expand Down Expand Up @@ -190,9 +224,15 @@ export const knownLibFilesForCompilerOptions = (compilerOptions: CompilerOptions
/**
* Sets up a Map with lib contents by grabbing the necessary files from
* the local copy of typescript via the file system.
*
* The first two args are un-used, but kept around so as to not cause a
* semver major bump for no gain to module users.
*/
export const createDefaultMapFromNodeModules = (compilerOptions: CompilerOptions, ts?: typeof import("typescript"), tsLibDirectory?: string) => {
const tsModule = ts || require("typescript")
export const createDefaultMapFromNodeModules = (
_compilerOptions: CompilerOptions,
_ts?: typeof import("typescript"),
tsLibDirectory?: string
) => {
const path = requirePath()
const fs = requireFS()

Expand All @@ -201,9 +241,11 @@ export const createDefaultMapFromNodeModules = (compilerOptions: CompilerOptions
return fs.readFileSync(path.join(lib, name), "utf8")
}

const libs = knownLibFilesForCompilerOptions(compilerOptions, tsModule)
const libFiles = fs.readdirSync(tsLibDirectory || path.dirname(require.resolve("typescript")))
const knownLibFiles = libFiles.filter(f => f.startsWith("lib.") && f.endsWith(".d.ts"))

const fsMap = new Map<string, string>()
libs.forEach(lib => {
knownLibFiles.forEach(lib => {
fsMap.set("/" + lib, getLib(lib))
})
return fsMap
Expand Down Expand Up @@ -286,9 +328,14 @@ export const createDefaultMapFromCDN = (

// Map the known libs to a node fetch promise, then return the contents
function uncached() {
return Promise.all(files.map(lib => fetchlike(prefix + lib).then(resp => resp.text()))).then(contents => {
contents.forEach((text, index) => fsMap.set("/" + files[index], text))
})
return (
Promise.all(files.map(lib => fetchlike(prefix + lib).then(resp => resp.text())))
.then(contents => {
contents.forEach((text, index) => fsMap.set("/" + files[index], text))
})
// Return a NOOP for .d.ts files which aren't in the current build of TypeScript
Copy link
Member

Choose a reason for hiding this comment

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

Hm, looking back, is this right? Won't this fail if any of them fail?

.catch(() => { })
)
}

// A localstorage and lzzip aware version of the lib files
Expand All @@ -310,20 +357,26 @@ export const createDefaultMapFromCDN = (

if (!content) {
// Make the API call and store the text concent in the cache
return fetchlike(prefix + lib)
.then(resp => resp.text())
.then(t => {
storelike.setItem(cacheKey, zip(t))
return t
})
return (
fetchlike(prefix + lib)
.then(resp => resp.text())
.then(t => {
storelike.setItem(cacheKey, zip(t))
return t
})
// Return a NOOP for .d.ts files which aren't in the current build of TypeScript
.catch(() => { })
)
} else {
return Promise.resolve(unzip(content))
}
})
).then(contents => {
contents.forEach((text, index) => {
const name = "/" + files[index]
fsMap.set(name, text)
if (text) {
const name = "/" + files[index]
fsMap.set(name, text)
}
})
})
}
Expand Down Expand Up @@ -403,7 +456,12 @@ export function createSystem(files: Map<string, string>): System {
* a set of virtual files which are prioritised over the FS versions, then a path to the root of your
* project (basically the folder your node_modules lives)
*/
export function createFSBackedSystem(files: Map<string, string>, _projectRoot: string, ts: TS, tsLibDirectory?: string): System {
export function createFSBackedSystem(
files: Map<string, string>,
_projectRoot: string,
ts: TS,
tsLibDirectory?: string
): System {
// We need to make an isolated folder for the tsconfig, but also need to be able to resolve the
// existing node_modules structures going back through the history
const root = _projectRoot + "/vfs"
Expand Down
5 changes: 5 additions & 0 deletions packages/typescript-vfs/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,8 @@ it("throws when you request a lib file which isn't in the fsMap", () => {

expect(t).toThrow()
})

it("grabs lib dts files from node_modules", async () => {
const fsMap = createDefaultMapFromNodeModules({})
expect(fsMap.get("/lib.es2015.collection.d.ts")).toBeDefined()
})