0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-04 10:39:43 +08:00
discourse/frontend/asset-processor/rollup-virtual-imports.js
David Taylor de793bc6ed
DEV: add typescript support for core, themes and plugins (#41478)
We've been using basic type-checking via JSDoc for some time. This
commit allows us to author proper `.ts`/`.gts` files, and use the full
typescript syntax. Initially, only d-button and a single chat file are
migrated, as proof of functionality.

In future, we may migrate more files, and consider making our tsconfig
more strict.
2026-07-08 12:57:18 +01:00

116 lines
2.9 KiB
JavaScript
Vendored

const SUPPORTED_FILE_EXTENSIONS = [
".js",
".js.es6",
".hbs",
".gjs",
".ts",
".gts",
];
const IS_CONNECTOR_REGEX = /(^|\/)connectors\//;
export default {
"virtual:entrypoint": (moduleFilenames, { themeId, pluginName }) => {
const label = pluginName ? `PLUGIN ${pluginName}` : `THEME ${themeId}`;
const imports = [];
const entries = [];
const warnings = [];
const exportedModules = new Set();
let i = 1;
for (const moduleFilename of moduleFilenames) {
// Type-only declaration files have no runtime module to export.
if (moduleFilename.endsWith(".d.ts")) {
continue;
}
if (
!SUPPORTED_FILE_EXTENSIONS.some((ext) => moduleFilename.endsWith(ext))
) {
// Unsupported file type. Log a warning and skip
warnings.push(
`console.warn("[${label}] Unsupported file type: ${moduleFilename}");`
);
continue;
}
const filenameWithoutExtension = moduleFilename.replace(
/\.[^\.]+(\.es6)?$/,
""
);
let compatModuleName = filenameWithoutExtension;
if (moduleFilename.match(IS_CONNECTOR_REGEX)) {
const isTemplate = moduleFilename.endsWith(".hbs");
const isInTemplatesDirectory =
moduleFilename.match(/(^|\/)templates\//);
if (isTemplate && !isInTemplatesDirectory) {
compatModuleName = compatModuleName.replace(
IS_CONNECTOR_REGEX,
"$1templates/connectors/"
);
} else if (!isTemplate && isInTemplatesDirectory) {
compatModuleName = compatModuleName.replace(
/(^|\/)templates\//,
"$1"
);
}
}
const importPath = filenameWithoutExtension.match(IS_CONNECTOR_REGEX)
? moduleFilename
: filenameWithoutExtension;
if (exportedModules.has(importPath)) {
continue;
}
exportedModules.add(importPath);
imports.push(`import * as Mod${i} from "./${importPath}";`);
entries.push(` "${compatModuleName}": Mod${i},`);
i += 1;
}
return [
...imports,
...warnings,
"const compatModules = {",
...entries,
"};",
"export default compatModules;",
"",
].join("\n");
},
"virtual:theme": ({ themeId }) => {
return cleanMultiline(`
import { getObjectForTheme } from "discourse/lib/theme-settings-store";
export const settings = getObjectForTheme(${themeId});
export function themePrefix(key) {
return \`theme_translations.${themeId}.\${key}\`;
}
`);
},
};
function cleanMultiline(str) {
const lines = str.split("\n");
if (lines.at(0).trim() === "") {
lines.shift();
}
if (lines.at(-1).trim() === "") {
lines.pop();
}
const minLeadingWhitspace = Math.min(
...lines.filter(Boolean).map((line) => line.match(/^\s*/)[0].length)
);
return lines.map((line) => line.slice(minLeadingWhitspace)).join("\n") + "\n";
}