import { BuildConfig, panic } from './util';
import { Extractor, ExtractorConfig } from '@microsoft/api-extractor';
import { join } from 'path';
import { readFileSync, writeFileSync } from 'fs';
/**
* Create each submodule's bundled dts file, and ensure
* the public API has not changed for a production build.
*/
export function apiExtractor(config: BuildConfig) {
// core
// Run the api extractor for each of the submodules
createTypesApi(
config,
join(config.srcDir, 'core'),
join(config.distPkgDir, 'core.d.ts'),
'./core'
);
createTypesApi(
config,
join(config.srcDir, 'jsx-runtime'),
join(config.distPkgDir, 'jsx-runtime.d.ts'),
'./core'
);
createTypesApi(
config,
join(config.srcDir, 'optimizer'),
join(config.distPkgDir, 'optimizer.d.ts'),
'./core'
);
createTypesApi(
config,
join(config.srcDir, 'server'),
join(config.distPkgDir, 'server.d.ts'),
'./core'
);
createTypesApi(
config,
join(config.srcDir, 'testing'),
join(config.distPkgDir, 'testing', 'index.d.ts'),
'../core'
);
createTypesApi(
config,
join(config.srcDir, 'build'),
join(config.distPkgDir, 'build', 'index.d.ts'),
'../core'
);
generateServerReferenceModules(config);
// qwik-city
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'runtime', 'src'),
join(config.packagesDir, 'qwik-city', 'lib', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'buildtime', 'vite'),
join(config.packagesDir, 'qwik-city', 'lib', 'vite', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'middleware', 'cloudflare-pages'),
join(config.packagesDir, 'qwik-city', 'lib', 'middleware', 'cloudflare-pages', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'middleware', 'express'),
join(config.packagesDir, 'qwik-city', 'lib', 'middleware', 'express', 'index.d.ts')
);
createTypesApi(
config,
join(config.packagesDir, 'qwik-city', 'middleware', 'netlify-edge'),
join(config.packagesDir, 'qwik-city', 'lib', 'middleware', 'netlify-edge', 'index.d.ts')
);
generateQwikCityReferenceModules(config);
console.log('🥶', 'submodule d.ts API files generated');
}
function createTypesApi(config: BuildConfig, inPath: string, outPath: string, corePath?: string) {
const extractorConfigPath = join(inPath, 'api-extractor.json');
const extractorConfig = ExtractorConfig.loadFileAndPrepare(extractorConfigPath);
const result = Extractor.invoke(extractorConfig, {
localBuild: !!config.dev,
showVerboseMessages: true,
showDiagnostics: true,
messageCallback(msg) {
msg.handled = true;
if (msg.logLevel === 'verbose' || msg.logLevel === 'warning') {
return;
}
if (msg.text.includes('Analysis will use')) {
return;
}
if (msg.messageId === 'console-compiler-version-notice') {
return;
}
console.error(`❌ API Extractor, submodule: "${inPath}"\n${extractorConfigPath}\n`, msg);
},
});
if (!result.succeeded) {
panic(
`Use "yarn api.update" to automatically update the .md files if the api changes were expected`
);
}
const srcPath = result.extractorConfig.untrimmedFilePath;
const content = fixDtsContent(config, srcPath, corePath);
writeFileSync(outPath, content);
}
function generateQwikCityReferenceModules(config: BuildConfig) {
// server-modules.d.ts
const referenceDts = `
declare module '@qwik-city-plan' {
const qwikCityPlan: any;
export default qwikCityPlan;
}
`;
const srcModulesPath = join(config.packagesDir, 'qwik-city', 'lib');
const destModulesPath = join(srcModulesPath, 'modules.d.ts');
writeFileSync(destModulesPath, referenceDts);
// manually prepend the ts reference since api extractor removes it
const prependReferenceDts = `/// \n\n`;
const distIndexPath = join(srcModulesPath, 'index.d.ts');
let serverDts = readFileSync(distIndexPath, 'utf-8');
serverDts = prependReferenceDts + serverDts;
writeFileSync(distIndexPath, serverDts);
}
function generateServerReferenceModules(config: BuildConfig) {
// server-modules.d.ts
const referenceDts = `///
declare module '@qwik-client-manifest' {
const manifest: QwikManifest;
export { manifest };
}
`;
const destServerModulesPath = join(config.distPkgDir, 'server-modules.d.ts');
writeFileSync(destServerModulesPath, referenceDts);
// manually prepend the ts reference since api extractor removes it
const prependReferenceDts = `/// \n\n`;
const distServerPath = join(config.distPkgDir, 'server.d.ts');
let serverDts = readFileSync(distServerPath, 'utf-8');
serverDts = prependReferenceDts + serverDts;
writeFileSync(distServerPath, serverDts);
}
/**
* Fix up the generated dts content, and ensure it's using a relative
* path to find the core.d.ts file, rather than node resolving it.
*/
function fixDtsContent(config: BuildConfig, srcPath: string, corePath: string | undefined) {
let dts = readFileSync(srcPath, 'utf-8');
// ensure we're just using a relative path
if (corePath) {
dts = dts.replace(/@builder\.io\/qwik/g, corePath);
}
// for some reason api-extractor is adding this in ¯\_(ツ)_/¯
dts = dts.replace('{};', '');
// replace QWIK_VERSION with the actual version number, useful for debugging
return dts.replace(/QWIK_VERSION/g, config.distVersion);
}