test: use ts compiler in cli tests (#62783)

This commit is contained in:
Oliver Eyton-Williams
2025-11-25 16:26:54 +01:00
committed by GitHub
parent c3dcec1ac7
commit b11a297a2a
6 changed files with 146 additions and 70 deletions
+2
View File
@@ -49,6 +49,7 @@
"@types/js-yaml": "4.0.5",
"@types/polka": "^0.5.7",
"@types/string-similarity": "^4.0.2",
"@typescript/vfs-1.6.1": "npm:@typescript/vfs@1.6.1",
"@vitest/ui": "^3.2.4",
"eslint": "^9.39.1",
"glob": "8.1.0",
@@ -64,6 +65,7 @@
"puppeteer": "22.12.1",
"sirv": "^3.0.2",
"string-similarity": "4.0.4",
"typescript-5.9.2": "npm:typescript@5.9.2",
"vitest": "^3.2.4"
}
}
+41 -8
View File
@@ -1,11 +1,8 @@
import { describe, it, beforeAll, expect } from 'vitest';
import { describe, it, beforeAll, expect, vi } from 'vitest';
import jsdom from 'jsdom';
import lodash from 'lodash';
import {
buildChallenge,
runnerTypes
} from '../../../client/src/templates/Challenges/utils/build';
import {
challengeTypes,
hasNoSolution
@@ -28,6 +25,32 @@ import { sortChallenges } from './utils/sort-challenges.js';
const { flatten, isEmpty, cloneDeep } = lodash;
vi.mock(
'../../../client/src/templates/Challenges/utils/typescript-worker-handler',
async importOriginal => {
const actual = await importOriginal();
// ts and tsvfs must match the versions used in the typescript-worker.
const tsvfs = await import('@typescript/vfs-1.6.1');
const ts = await import('typescript-5.9.2');
// use the same TS compiler as the client
const tsCompiler = await import(
'../../../tools/client-plugins/browser-scripts/modules/typescript-compiler'
);
const compiler = new tsCompiler.Compiler(ts, tsvfs);
await compiler.setup({ useNodeModules: true });
return {
...actual,
checkTSServiceIsReady: () => Promise.resolve(true),
compileTypeScriptCode: code => {
const { result, error } = compiler.compile(code, 'index.tsx');
if (error) throw error;
return result;
}
};
}
);
const dom = new jsdom.JSDOM('');
global.document = dom.window.document;
global.DOMParser = dom.window.DOMParser;
@@ -86,13 +109,13 @@ export async function defineTestsForBlock(testFilter) {
const challengeData = { meta, challenges, lang };
describe('Check challenges', () => {
describe('Check challenges', async () => {
beforeAll(async () => {
page = await newPageContext();
global.Worker = createPseudoWorker(page);
});
populateTestsForLang(challengeData, () => page);
await populateTestsForLang(challengeData, () => page);
});
}
@@ -123,7 +146,13 @@ export async function getChallenges(lang, filters) {
return sortChallenges(challenges);
}
function populateTestsForLang({ lang, challenges, meta }) {
async function populateTestsForLang({ lang, challenges, meta }) {
// We have to dynamically import this because otherwise it will not be mocked.
// Presumably this is because we import from_this file in the generated block
// test files and that happens before the mock is applied.
const { buildChallenge } = await import(
'../../../client/src/templates/Challenges/utils/build'
);
const validateChallenge = challengeSchemaValidator();
describe(`Language: ${lang}`, function () {
@@ -341,6 +370,10 @@ async function createTestRunner(
buildChallenge,
solutionFromNext
) {
const { runnerTypes } = await import(
'../../../client/src/templates/Challenges/utils/build'
);
const challengeFiles = replaceChallengeFilesContentsWithSolutions(
challenge.challengeFiles,
solutionFiles
+83 -44
View File
@@ -121,7 +121,7 @@ importers:
version: 1.3.1
'@prisma/client':
specifier: 6.16.2
version: 6.16.2(prisma@6.16.2(typescript@5.8.2))(typescript@5.8.2)
version: 6.16.2(prisma@6.16.2(typescript@5.9.3))(typescript@5.9.3)
'@sentry/node':
specifier: 9.1.0
version: 9.1.0
@@ -230,10 +230,10 @@ importers:
version: 26.1.0
msw:
specifier: ^2.7.0
version: 2.8.7(@types/node@20.12.8)(typescript@5.8.2)
version: 2.8.7(@types/node@20.12.8)(typescript@5.9.3)
prisma:
specifier: 6.16.2
version: 6.16.2(typescript@5.8.2)
version: 6.16.2(typescript@5.9.3)
supertest:
specifier: 6.3.3
version: 6.3.3
@@ -242,7 +242,7 @@ importers:
version: 4.19.1
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
client:
dependencies:
@@ -703,6 +703,9 @@ importers:
'@types/string-similarity':
specifier: ^4.0.2
version: 4.0.2
'@typescript/vfs-1.6.1':
specifier: npm:@typescript/vfs@1.6.1
version: '@typescript/vfs@1.6.1(typescript@5.9.3)'
'@vitest/ui':
specifier: ^3.2.4
version: 3.2.4(vitest@3.2.4)
@@ -741,16 +744,19 @@ importers:
version: 0.5.2
puppeteer:
specifier: 22.12.1
version: 22.12.1(typescript@5.8.2)
version: 22.12.1(typescript@5.9.3)
sirv:
specifier: ^3.0.2
version: 3.0.2
string-similarity:
specifier: 4.0.4
version: 4.0.4
typescript-5.9.2:
specifier: npm:typescript@5.9.2
version: typescript@5.9.2
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@16.7.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@16.7.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
e2e:
devDependencies:
@@ -834,7 +840,7 @@ importers:
version: 9.39.1(jiti@2.6.1)
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
tools/challenge-editor/api:
dependencies:
@@ -1062,13 +1068,13 @@ importers:
version: 3.0.4
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
tools/client-plugins/browser-scripts:
dependencies:
'@freecodecamp/curriculum-helpers':
specifier: ^7.1.0
version: 7.1.0(debug@4.3.4)(typescript@5.7.3)
version: 7.1.0(debug@4.3.4)(typescript@5.9.2)
xterm:
specifier: ^5.2.1
version: 5.3.0
@@ -1092,8 +1098,8 @@ importers:
specifier: ^8.0.1
version: 8.0.1(webpack-cli@4.10.0)
'@typescript/vfs':
specifier: ^1.6.0
version: 1.6.1(typescript@5.7.3)
specifier: 1.6.1
version: 1.6.1(typescript@5.9.2)
babel-loader:
specifier: 8.3.0
version: 8.3.0(@babel/core@7.28.5)(webpack@5.90.3)
@@ -1112,6 +1118,9 @@ importers:
sass.js:
specifier: 0.11.1
version: 0.11.1
typescript:
specifier: 5.9.2
version: 5.9.2
util:
specifier: 0.12.5
version: 0.12.5
@@ -1177,7 +1186,7 @@ importers:
version: 9.39.1(jiti@2.6.1)
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
tools/scripts/seed:
devDependencies:
@@ -13091,6 +13100,16 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
typescript@5.9.2:
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
uc.micro@2.0.0:
resolution: {integrity: sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==}
@@ -17098,7 +17117,7 @@ snapshots:
prop-types: 15.8.1
react: 17.0.2
'@freecodecamp/curriculum-helpers@7.1.0(debug@4.3.4)(typescript@5.7.3)':
'@freecodecamp/curriculum-helpers@7.1.0(debug@4.3.4)(typescript@5.9.2)':
dependencies:
'@sinonjs/fake-timers': 14.0.0
'@types/jquery': 3.5.32
@@ -17111,10 +17130,10 @@ snapshots:
http-server: 14.1.1(debug@4.3.4)
jquery: 3.7.1
process: 0.11.10
puppeteer: 24.10.0(typescript@5.7.3)
puppeteer: 24.10.0(typescript@5.9.2)
pyodide: 0.23.3
util: 0.12.5
vitest-environment-puppeteer: 11.0.3(debug@4.3.4)(typescript@5.7.3)
vitest-environment-puppeteer: 11.0.3(debug@4.3.4)(typescript@5.9.2)
transitivePeerDependencies:
- bare-buffer
- bufferutil
@@ -17902,10 +17921,10 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@prisma/client@6.16.2(prisma@6.16.2(typescript@5.8.2))(typescript@5.8.2)':
'@prisma/client@6.16.2(prisma@6.16.2(typescript@5.9.3))(typescript@5.9.3)':
optionalDependencies:
prisma: 6.16.2(typescript@5.8.2)
typescript: 5.8.2
prisma: 6.16.2(typescript@5.9.3)
typescript: 5.9.3
'@prisma/config@6.16.2':
dependencies:
@@ -19609,10 +19628,17 @@ snapshots:
'@typescript-eslint/types': 8.47.0
eslint-visitor-keys: 4.2.1
'@typescript/vfs@1.6.1(typescript@5.7.3)':
'@typescript/vfs@1.6.1(typescript@5.9.2)':
dependencies:
debug: 4.3.4(supports-color@8.1.1)
typescript: 5.7.3
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript/vfs@1.6.1(typescript@5.9.3)':
dependencies:
debug: 4.3.4(supports-color@8.1.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -19728,13 +19754,13 @@ snapshots:
msw: 2.8.7(@types/node@20.12.8)(typescript@5.7.3)
vite: 7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
'@vitest/mocker@3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))':
'@vitest/mocker@3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
msw: 2.8.7(@types/node@20.12.8)(typescript@5.8.2)
msw: 2.8.7(@types/node@20.12.8)(typescript@5.9.3)
vite: 7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
'@vitest/pretty-format@3.2.4':
@@ -19766,7 +19792,7 @@ snapshots:
sirv: 3.0.2
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.7.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1)
'@vitest/utils@3.2.4':
dependencies:
@@ -21380,14 +21406,23 @@ snapshots:
optionalDependencies:
typescript: 5.7.3
cosmiconfig@9.0.0(typescript@5.8.2):
cosmiconfig@9.0.0(typescript@5.9.2):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.0
js-yaml: 4.1.0
parse-json: 5.2.0
optionalDependencies:
typescript: 5.8.2
typescript: 5.9.2
cosmiconfig@9.0.0(typescript@5.9.3):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.0
js-yaml: 4.1.0
parse-json: 5.2.0
optionalDependencies:
typescript: 5.9.3
create-ecdh@4.0.4:
dependencies:
@@ -22365,7 +22400,7 @@ snapshots:
confusing-browser-globals: 1.0.11
eslint: 7.32.0
eslint-plugin-flowtype: 5.10.0(eslint@7.32.0)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react: 7.37.4(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react-hooks: 4.6.0(eslint@9.39.1(jiti@2.6.1))
@@ -22395,7 +22430,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)):
eslint-module-utils@2.12.0(@typescript-eslint/parser@4.33.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -22449,7 +22484,7 @@ snapshots:
- typescript
- utf-8-validate
eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1)):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -22460,7 +22495,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.1(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1))
eslint-module-utils: 2.12.0(@typescript-eslint/parser@4.33.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -23683,7 +23718,7 @@ snapshots:
eslint-config-react-app: 6.0.0(@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint@7.32.0)(typescript@5.2.2))(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(babel-eslint@10.1.0(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-flowtype@5.10.0(eslint@7.32.0))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint@7.32.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.0(eslint@7.32.0))(eslint-plugin-react@7.37.4(eslint@7.32.0))(eslint@7.32.0)(typescript@5.2.2)
eslint-plugin-flowtype: 5.10.0(eslint@7.32.0)
eslint-plugin-graphql: 4.0.0(@types/node@20.12.8)(graphql@15.8.0)(typescript@5.2.2)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@4.33.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.2.2))(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react: 7.37.4(eslint@9.39.1(jiti@2.6.1))
eslint-plugin-react-hooks: 4.6.0(eslint@9.39.1(jiti@2.6.1))
@@ -26479,7 +26514,7 @@ snapshots:
- '@types/node'
optional: true
msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2):
msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3):
dependencies:
'@bundled-es-modules/cookie': 2.0.1
'@bundled-es-modules/statuses': 1.0.1
@@ -26500,7 +26535,7 @@ snapshots:
type-fest: 4.37.0
yargs: 17.7.2
optionalDependencies:
typescript: 5.8.2
typescript: 5.9.3
transitivePeerDependencies:
- '@types/node'
@@ -27449,12 +27484,12 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.2.0
prisma@6.16.2(typescript@5.8.2):
prisma@6.16.2(typescript@5.9.3):
dependencies:
'@prisma/config': 6.16.2
'@prisma/engines': 6.16.2
optionalDependencies:
typescript: 5.8.2
typescript: 5.9.3
transitivePeerDependencies:
- magicast
@@ -27592,10 +27627,10 @@ snapshots:
- supports-color
- utf-8-validate
puppeteer@22.12.1(typescript@5.8.2):
puppeteer@22.12.1(typescript@5.9.3):
dependencies:
'@puppeteer/browsers': 2.2.3
cosmiconfig: 9.0.0(typescript@5.8.2)
cosmiconfig: 9.0.0(typescript@5.9.3)
devtools-protocol: 0.0.1299070
puppeteer-core: 22.12.1
transitivePeerDependencies:
@@ -27605,11 +27640,11 @@ snapshots:
- typescript
- utf-8-validate
puppeteer@24.10.0(typescript@5.7.3):
puppeteer@24.10.0(typescript@5.9.2):
dependencies:
'@puppeteer/browsers': 2.10.5
chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169)
cosmiconfig: 9.0.0(typescript@5.7.3)
cosmiconfig: 9.0.0(typescript@5.9.2)
devtools-protocol: 0.0.1452169
puppeteer-core: 24.10.0
typed-query-selector: 2.12.0
@@ -29806,6 +29841,10 @@ snapshots:
typescript@5.8.2: {}
typescript@5.9.2: {}
typescript@5.9.3: {}
uc.micro@2.0.0: {}
umd@3.0.3: {}
@@ -30283,21 +30322,21 @@ snapshots:
transitivePeerDependencies:
- debug
vitest-environment-puppeteer@11.0.3(debug@4.3.4)(typescript@5.7.3):
vitest-environment-puppeteer@11.0.3(debug@4.3.4)(typescript@5.9.2):
dependencies:
chalk: 4.1.2
cosmiconfig: 9.0.0(typescript@5.7.3)
cosmiconfig: 9.0.0(typescript@5.9.2)
deepmerge: 4.3.1
vitest-dev-server: 11.0.3(debug@4.3.4)
transitivePeerDependencies:
- debug
- typescript
vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@16.7.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1):
vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@16.7.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))
'@vitest/mocker': 3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -30425,11 +30464,11 @@ snapshots:
- tsx
- yaml
vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1):
vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.12.8)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@26.1.0)(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.2
'@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.8.2))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))
'@vitest/mocker': 3.2.4(msw@2.8.7(@types/node@20.12.8)(typescript@5.9.3))(vite@7.1.3(@types/node@20.12.8)(jiti@2.6.1)(terser@5.28.1)(tsx@4.19.1)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -11,15 +11,12 @@ export class Compiler {
tsvfs: TSVFS;
tsEnv?: VirtualTypeScriptEnvironment;
compilerHost?: CompilerHost;
constructor(
ts: typeof import('typescript'),
tsvfs: typeof import('@typescript/vfs')
) {
constructor(ts: TS, tsvfs: TSVFS) {
this.ts = ts;
this.tsvfs = tsvfs;
}
async setup() {
async setup(opts?: { useNodeModules: boolean }) {
const ts = this.ts;
const tsvfs = this.tsvfs;
@@ -33,17 +30,21 @@ export class Compiler {
jsx: ts.JsxEmit.Preserve, // Babel will handle JSX,
allowUmdGlobalAccess: true // Necessary because React is loaded via a UMD script.
};
const fsMap = await tsvfs.createDefaultMapFromCDN(
compilerOptions,
ts.version,
false, // TODO: cache this. It needs a store that's available to workers and implements https://github.com/microsoft/TypeScript-Website/blob/ac68b8b8e4a621113c4ee45c4051002fd55ede24/packages/typescript-vfs/src/index.ts#L11
ts
);
const fsMap = opts?.useNodeModules
? tsvfs.createDefaultMapFromNodeModules(compilerOptions, ts)
: await tsvfs.createDefaultMapFromCDN(
compilerOptions,
ts.version,
false, // TODO: cache this. It needs a store that's available to workers and implements https://github.com/microsoft/TypeScript-Website/blob/ac68b8b8e4a621113c4ee45c4051002fd55ede24/packages/typescript-vfs/src/index.ts#L11
ts
);
// This can be any path, but doing this means import React from 'react' works, if we ever need it.
const reactTypesPath = `/node_modules/@types/react/index.d.ts`;
// It may be necessary to get all the types (global.d.ts etc)
fsMap.set(reactTypesPath, reactTypes['react-18'] || '');
const system = tsvfs.createSystem(fsMap);
@@ -65,10 +66,14 @@ export class Compiler {
).compilerHost;
}
compile(code: string, fileName: string) {
compile(rawCode: string, fileName: string) {
if (!this.tsEnv || !this.compilerHost) {
throw Error('TypeScript environment not set up');
}
// If we try to update or create an empty file, the environment will become
// permanently unable to interact with that file. The workaround is to create
// a file with a single newline character.
const code = rawCode || '\n';
// TODO: If creating the file fresh each time is too slow, we can try checking
// if the file exists and updating it if it does.
this.tsEnv.createFile(fileName, code);
@@ -32,13 +32,14 @@
"@babel/preset-typescript": "7.23.3",
"@freecodecamp/eslint-config": "workspace:*",
"@types/copy-webpack-plugin": "^8.0.1",
"@typescript/vfs": "^1.6.0",
"@typescript/vfs": "1.6.1",
"babel-loader": "8.3.0",
"copy-webpack-plugin": "9.1.0",
"eslint": "^9.39.1",
"process": "0.11.10",
"pyodide": "^0.23.3",
"sass.js": "0.11.1",
"typescript": "5.9.2",
"util": "0.12.5",
"webpack": "5.90.3",
"webpack-cli": "4.10.0"
@@ -92,11 +92,7 @@ async function handleCheckIsReadyRequest(port: MessagePort) {
}
function handleCompileRequest(data: TSCompileEvent['data'], port: MessagePort) {
// If we try to update or create an empty file, the environment will become
// permanently unable to interact with that file. The workaround is to create
// a file with a single newline character.
const code = (data.code || '').slice() || '\n';
const { result, error } = compiler.compile(code, 'index.tsx');
const { result, error } = compiler.compile(data.code, 'index.tsx');
const message: TSCompiledMessage = {
type: 'compiled',
value: result,