@@ -1034,6 +1034,17 @@ export const buildAPI = task({
10341034 } ,
10351035} ) ;
10361036
1037+ async function runCheckAPIDeclarations ( ) {
1038+ await run ( "node" , [ "./node_modules/typescript/bin/tsc" , "-p" , "packages/typescript/tsconfig.dist.json" ] ) ;
1039+ }
1040+
1041+ export const checkAPIDeclarations = task ( {
1042+ name : "check:api-declarations" ,
1043+ description : "Type-checks the emitted @typescript/typescript declaration files." ,
1044+ dependencies : [ buildAPI ] ,
1045+ run : runCheckAPIDeclarations ,
1046+ } ) ;
1047+
10371048async function runBuildAPITests ( generateSources = true ) {
10381049 if ( generateSources ) await runGenerateSync ( ) ;
10391050 await run ( "npm" , [ "run" , "-w" , "@typescript/typescript" , "build:test" ] ) ;
@@ -1221,6 +1232,7 @@ export const validate = task({
12211232
12221233 await runValidation ( "test:tsc" , runTests ) ;
12231234 await runValidation ( "test:extension" , runTestExtension ) ;
1235+ await runValidation ( "check:api-declarations" , runCheckAPIDeclarations ) ;
12241236 if ( options . api || options . all ) {
12251237 if ( ! options . all ) await runGenerateEnums ( ) ;
12261238 await runBuildAPITests ( ! options . all ) ;
@@ -2343,6 +2355,7 @@ async function runBuildNativePreviewPackages() {
23432355
23442356 // Build JS API and copy dist into the package.
23452357 await run ( "npm" , [ "run" , "-w" , "@typescript/typescript" , "build" ] ) ;
2358+ await runCheckAPIDeclarations ( ) ;
23462359 await cpRecursive ( path . join ( inputDir , "dist" ) , path . join ( mainPackageDir , "dist" ) ) ;
23472360
23482361 // Validate that .d.ts files contain no external imports (all imports must start with "." or "#").
@@ -2673,38 +2686,42 @@ async function runPackNativePreviewPackages() {
26732686export const packVsixExtensions = task ( {
26742687 name : "vscode-typescript:pack" ,
26752688 hiddenFromTaskList : true ,
2676- dependencies : options . forRelease || usePublishedPlatformPackagesForVsix ? undefined : [ buildNativePreviewPackages , cleanSignTempDirectory ] ,
2689+ dependencies : options . forRelease || usePublishedPlatformPackagesForVsix ? undefined : [ packNativePreviewPackages ] ,
26772690 run : runPackVsixExtensions ,
26782691} ) ;
26792692
26802693/** @type {Map<string, Promise<string>> } */
2681- const publishedPlatformPackageLibDirs = new Map ( ) ;
2694+ const publishedPlatformPackageDirs = new Map ( ) ;
26822695
2683- const getPublishedTypeScriptPackageJson = memoize ( ( ) => {
2696+ const getPublishedTypeScriptPackageDir = memoize ( ( ) => {
26842697 const candidates = [
2685- path . join ( extensionDir , "node_modules" , publishedTypeScriptAliasPackageName , "package.json" ) ,
2686- path . join ( __dirname , "node_modules" , publishedTypeScriptAliasPackageName , "package.json" ) ,
2698+ path . join ( extensionDir , "node_modules" , publishedTypeScriptAliasPackageName ) ,
2699+ path . join ( __dirname , "node_modules" , publishedTypeScriptAliasPackageName ) ,
26872700 ] ;
26882701
26892702 for ( const candidate of candidates ) {
2690- if ( fs . existsSync ( candidate ) ) {
2691- const packageJson = JSON . parse ( fs . readFileSync ( candidate , "utf8" ) ) ;
2692- if ( packageJson . name !== "typescript" ) {
2693- throw new Error ( `${ publishedTypeScriptAliasPackageName } should alias the typescript package, but found ${ packageJson . name } .` ) ;
2694- }
2695- if ( ! packageJson . version || typeof packageJson . version !== "string" ) {
2696- throw new Error ( `${ publishedTypeScriptAliasPackageName } package.json did not contain a version.` ) ;
2697- }
2698- if ( ! packageJson . optionalDependencies || typeof packageJson . optionalDependencies !== "object" ) {
2699- throw new Error ( `${ publishedTypeScriptAliasPackageName } package.json did not contain platform optionalDependencies.` ) ;
2700- }
2701- return packageJson ;
2703+ if ( fs . existsSync ( path . join ( candidate , "package.json" ) ) ) {
2704+ return candidate ;
27022705 }
27032706 }
27042707
27052708 throw new Error ( `Could not find ${ publishedTypeScriptAliasPackageName } ; run npm install first.` ) ;
27062709} ) ;
27072710
2711+ const getPublishedTypeScriptPackageJson = memoize ( ( ) => {
2712+ const packageJson = JSON . parse ( fs . readFileSync ( path . join ( getPublishedTypeScriptPackageDir ( ) , "package.json" ) , "utf8" ) ) ;
2713+ if ( packageJson . name !== "typescript" ) {
2714+ throw new Error ( `${ publishedTypeScriptAliasPackageName } should alias the typescript package, but found ${ packageJson . name } .` ) ;
2715+ }
2716+ if ( ! packageJson . version || typeof packageJson . version !== "string" ) {
2717+ throw new Error ( `${ publishedTypeScriptAliasPackageName } package.json did not contain a version.` ) ;
2718+ }
2719+ if ( ! packageJson . optionalDependencies || typeof packageJson . optionalDependencies !== "object" ) {
2720+ throw new Error ( `${ publishedTypeScriptAliasPackageName } package.json did not contain platform optionalDependencies.` ) ;
2721+ }
2722+ return packageJson ;
2723+ } ) ;
2724+
27082725function getPublishedTypeScriptVersion ( ) {
27092726 const version = getPublishedTypeScriptPackageJson ( ) . version ;
27102727 if ( releaseVscodeTypescript && ! stableThreeComponentVersionPattern . test ( version ) ) {
@@ -2725,23 +2742,23 @@ const getPackageLock = memoize(() => JSON.parse(fs.readFileSync(path.join(__dirn
27252742/**
27262743 * @param {string } npmPackageName
27272744 */
2728- async function getPublishedPlatformPackageLibDir ( npmPackageName ) {
2729- let promise = publishedPlatformPackageLibDirs . get ( npmPackageName ) ;
2745+ async function getPublishedPlatformPackageDir ( npmPackageName ) {
2746+ let promise = publishedPlatformPackageDirs . get ( npmPackageName ) ;
27302747 if ( ! promise ) {
2731- promise = getPublishedPlatformPackageLibDirWorker ( npmPackageName ) ;
2732- publishedPlatformPackageLibDirs . set ( npmPackageName , promise ) ;
2748+ promise = getPublishedPlatformPackageDirWorker ( npmPackageName ) ;
2749+ publishedPlatformPackageDirs . set ( npmPackageName , promise ) ;
27332750 }
27342751 return promise ;
27352752}
27362753
27372754/**
27382755 * @param {string } npmPackageName
27392756 */
2740- async function getPublishedPlatformPackageLibDirWorker ( npmPackageName ) {
2757+ async function getPublishedPlatformPackageDirWorker ( npmPackageName ) {
27412758 const dest = path . join ( builtPublishedPlatformPackages , "node_modules" , ...npmPackageName . split ( "/" ) ) ;
27422759 const lib = path . join ( dest , "lib" ) ;
27432760 if ( fs . existsSync ( lib ) ) {
2744- return lib ;
2761+ return dest ;
27452762 }
27462763
27472764 await fs . promises . mkdir ( dest , { recursive : true } ) ;
@@ -2799,26 +2816,27 @@ async function getPublishedPlatformPackageLibDirWorker(npmPackageName) {
27992816 throw new Error ( `Published platform package ${ npmPackageName } @${ version } did not contain a lib directory.` ) ;
28002817 }
28012818
2802- return lib ;
2819+ return dest ;
28032820}
28042821
28052822async function runPackVsixExtensions ( ) {
28062823 await rimraf ( builtVsix ) ;
28072824 await fs . promises . mkdir ( builtVsix , { recursive : true } ) ;
28082825 if ( usePublishedPlatformPackagesForVsix ) {
28092826 checkPublishedPlatformPackagesForVsix ( ) ;
2810- publishedPlatformPackageLibDirs . clear ( ) ;
2827+ publishedPlatformPackageDirs . clear ( ) ;
28112828 await rimraf ( builtPublishedPlatformPackages ) ;
28122829 }
28132830
28142831 const platforms = getPlatforms ( ) ;
2815- const extensions = platforms . flatMap ( ( { npmDir , npmPackageName, extensions } ) => extensions . map ( e => ( { npmDir , npmPackageName, ...e } ) ) ) ;
2832+ const extensions = platforms . flatMap ( ( { npmTarball , npmPackageName, extensions } ) => extensions . map ( e => ( { npmTarball , npmPackageName, ...e } ) ) ) ;
28162833 if ( ! extensions . length ) {
28172834 console . log ( "No VSIX targets configured; skipping extension packaging." ) ;
28182835 return ;
28192836 }
28202837
28212838 // We don't use vscode:prepublish, as that would run the build for each package below.
2839+ await run ( "npm" , [ "run" , "-w" , "@typescript/typescript" , "build" ] ) ;
28222840 await run ( "npm" , [ "run" , "bundle:release" ] , { cwd : extensionDir , env : releasePackageEnv } ) ;
28232841
28242842 let version = "0.0.0" ;
@@ -2844,29 +2862,43 @@ async function runPackVsixExtensions() {
28442862
28452863 console . log ( "Version:" , version ) ;
28462864
2847- await Promise . all ( extensions . map ( async ( { npmDir, npmPackageName, nodeOs, vscodeTarget, sourceDir, extensionDir : thisExtensionDir , vsixPath, vsixManifestPath, vsixSignaturePath } ) => {
2848- const npmLibDir = usePublishedPlatformPackagesForVsix
2849- ? await getPublishedPlatformPackageLibDir ( npmPackageName )
2850- : path . join ( npmDir , "lib" ) ;
2851- const extensionLibDir = path . join ( thisExtensionDir , "lib" ) ;
2852- await fs . promises . mkdir ( extensionLibDir , { recursive : true } ) ;
2865+ await Promise . all ( extensions . map ( async ( { npmTarball, npmPackageName, nodeOs, vscodeTarget, sourceDir, extensionDir : thisExtensionDir , vsixPath, vsixManifestPath, vsixSignaturePath } ) => {
2866+ const nodeModules = path . join ( thisExtensionDir , "node_modules" ) ;
2867+ const embeddedPlatformPackageDir = path . join ( nodeModules , ...npmPackageName . split ( "/" ) ) ;
2868+ const embeddedTypeScriptDir = path . join ( nodeModules , "typescript" ) ;
28532869
28542870 await cpWithoutNodeModulesOrTsconfig ( sourceDir , thisExtensionDir ) ;
2855- await cpWithoutNodeModulesOrTsconfig ( npmLibDir , extensionLibDir ) ;
2856- await fs . promises . chmod ( path . join ( extensionLibDir , nativePreviewExeName ( nodeOs ) ) , 0o755 ) ;
2871+ if ( usePublishedPlatformPackagesForVsix ) {
2872+ await cpRecursive ( await getPublishedPlatformPackageDir ( npmPackageName ) , embeddedPlatformPackageDir ) ;
2873+ await cpRecursive ( getPublishedTypeScriptPackageDir ( ) , embeddedTypeScriptDir , p => ! p . endsWith ( "/node_modules" ) ) ;
2874+ }
2875+ else {
2876+ await fs . promises . mkdir ( embeddedPlatformPackageDir , { recursive : true } ) ;
2877+ await fs . promises . mkdir ( embeddedTypeScriptDir , { recursive : true } ) ;
2878+ await tar . x ( { file : npmTarball , cwd : embeddedPlatformPackageDir , strip : 1 } ) ;
2879+ await tar . x ( { file : mainNativePreviewPackage . npmTarball , cwd : embeddedTypeScriptDir , strip : 1 } ) ;
2880+ }
2881+ await fs . promises . chmod ( path . join ( embeddedPlatformPackageDir , "lib" , nativePreviewExeName ( nodeOs ) ) , 0o755 ) ;
2882+ const embeddedTypeScriptPackageJson = JSON . parse ( await fs . promises . readFile ( path . join ( embeddedTypeScriptDir , "package.json" ) , "utf8" ) ) ;
28572883
28582884 const packageJsonPath = path . join ( thisExtensionDir , "package.json" ) ;
28592885 const packageJson = JSON . parse ( fs . readFileSync ( packageJsonPath , "utf8" ) ) ;
28602886 packageJson . version = version ;
28612887 packageJson . bundledTypeScriptVersion = usePublishedPlatformPackagesForVsix ? getPublishedTypeScriptVersion ( ) : getVersion ( ) ;
2888+ packageJson . dependencies = {
2889+ typescript : embeddedTypeScriptPackageJson . name === "typescript"
2890+ ? embeddedTypeScriptPackageJson . version
2891+ : `npm:${ embeddedTypeScriptPackageJson . name } @${ embeddedTypeScriptPackageJson . version } ` ,
2892+ } ;
28622893 fs . writeFileSync ( packageJsonPath , JSON . stringify ( packageJson , undefined , 4 ) ) ;
28632894
28642895 await fs . promises . copyFile ( "NOTICE.txt" , path . join ( thisExtensionDir , "NOTICE.txt" ) ) ;
28652896
2866- await run ( "vsce" , [ "package" , version , "--no-update-package-json" , "--no-dependencies " , "--out" , vsixPath , "--target" , vscodeTarget ] , {
2897+ await run ( "vsce" , [ "package" , version , "--no-update-package-json" , "--no-yarn " , "--out" , vsixPath , "--target" , vscodeTarget ] , {
28672898 cwd : thisExtensionDir ,
28682899 env : releasePackageEnv ,
28692900 } ) ;
2901+ await testVsixPackage ( vsixPath , thisExtensionDir , npmPackageName , nodeOs , vscodeTarget ) ;
28702902
28712903 if ( options . forRelease ) {
28722904 await run ( "vsce" , [ "generate-manifest" , "--packagePath" , vsixPath , "--out" , vsixManifestPath ] , {
@@ -2878,6 +2910,64 @@ async function runPackVsixExtensions() {
28782910 } ) ) ;
28792911}
28802912
2913+ /**
2914+ * @param {string } vsixPath
2915+ * @param {string } extensionPath
2916+ * @param {string } platformPackageName
2917+ * @param {string } nodeOs
2918+ * @param {string } vscodeTarget
2919+ */
2920+ async function testVsixPackage ( vsixPath , extensionPath , platformPackageName , nodeOs , vscodeTarget ) {
2921+ const zip = new AdmZip ( vsixPath ) ;
2922+ for ( const packageName of [ "typescript" , platformPackageName ] ) {
2923+ const packagePath = path . join ( extensionPath , "node_modules" , ...packageName . split ( "/" ) ) ;
2924+ const files = await fs . promises . readdir ( packagePath , { recursive : true , withFileTypes : true } ) ;
2925+ for ( const file of files ) {
2926+ if ( ! file . isFile ( ) ) continue ;
2927+ const filePath = path . join ( file . parentPath , file . name ) ;
2928+ const archivePath = `extension/node_modules/${ packageName } /${ path . relative ( packagePath , filePath ) . replace ( / \\ / g, "/" ) } ` ;
2929+ const entry = zip . getEntry ( archivePath ) ;
2930+ assert ( entry , `VSIX is missing ${ archivePath } ` ) ;
2931+ assert . deepEqual ( entry . getData ( ) , await fs . promises . readFile ( filePath ) , `VSIX changed ${ archivePath } ` ) ;
2932+ }
2933+ }
2934+
2935+ const hostTarget = `${ process . platform } -${ process . arch === "arm" ? "armhf" : process . arch } ` ;
2936+ if ( vscodeTarget !== hostTarget ) return ;
2937+
2938+ const directory = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , "typescript-vsix-test-" ) ) ;
2939+ try {
2940+ zip . extractAllTo ( directory ) ;
2941+ const platformPath = path . join ( directory , "extension" , "node_modules" , ...platformPackageName . split ( "/" ) ) ;
2942+ await fs . promises . chmod ( path . join ( platformPath , "lib" , nativePreviewExeName ( nodeOs ) ) , 0o755 ) ;
2943+ await fs . promises . writeFile ( path . join ( directory , "tsconfig.json" ) , '{"files": []}\n' ) ;
2944+ await run ( process . execPath , [
2945+ "--input-type=module" ,
2946+ "--eval" ,
2947+ `
2948+ import assert from "node:assert/strict";
2949+ import { createRequire } from "node:module";
2950+ import { pathToFileURL } from "node:url";
2951+ const require = createRequire(process.cwd() + "/extension/node_modules/typescript/package.json");
2952+ const manifest = require("./package.json");
2953+ for (const mode of ["async", "sync"]) {
2954+ const exportPath = manifest.exports?.["./" + mode] ? mode : "unstable/" + mode;
2955+ const { API } = await import(pathToFileURL(require.resolve(manifest.name + "/" + exportPath)).href);
2956+ const api = new API();
2957+ try {
2958+ assert.ok(await api.parseConfigFile(process.cwd() + "/tsconfig.json"));
2959+ } finally {
2960+ await api.close();
2961+ }
2962+ }
2963+ ` ,
2964+ ] , { cwd : directory } ) ;
2965+ }
2966+ finally {
2967+ await fs . promises . rm ( directory , { recursive : true , force : true } ) ;
2968+ }
2969+ }
2970+
28812971export const signVsixExtensions = task ( {
28822972 name : "vscode-typescript:sign" ,
28832973 hiddenFromTaskList : true ,
0 commit comments