UNPKG

@inlang/paraglide-js

Version:

[![NPM Downloads](https://img.shields.io/npm/dw/%40inlang%2Fparaglide-js?logo=npm&logoColor=red&label=npm%20downloads)](https://www.npmjs.com/package/@inlang/paraglide-js) [![GitHub Issues](https://img.shields.io/github/issues-closed/opral/paraglide-js?lo

270 lines (269 loc) 9.24 kB
import { prompt } from "../utils.js"; import JSON5 from "json5"; import { pathExists } from "../../services/file-handling/exists.js"; import nodePath from "node:path"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); export const maybeUpdateTsConfig = async (ctx) => { const ctx1 = await maybeUpdateTsConfigAllowJs(ctx); return ctx1; // return await maybeUpdateTsConfigModuleResolution(ctx1); }; /** * Paraligde JS compiles to JS with JSDoc comments. TypeScript doesn't allow JS files by default. */ export const maybeUpdateTsConfigAllowJs = async (ctx) => { if ((await pathExists("./tsconfig.json", ctx.fs)) === false) { return ctx; } if (await hasAllowJsEnabled("./tsconfig.json", ctx.fs)) { // all clear, allowJs is already set to true return ctx; } ctx.logger.info(`The option \`compilerOptions.allowJs\` needs to be set to \`true\` in the \`tsconfig.json\` file: \`{ "compilerOptions": { "allowJs": true } }\``); let isValid = false; while (isValid === false) { const response = await prompt(`Is \`compilerOptions.allowJs\` set to \`true\`?`, { type: "confirm", initial: true, }); if (response === false) { ctx.logger.warn("Continuing without adjusting the tsconfig.json. This may lead to type errors."); return ctx; } if (await hasAllowJsEnabled("./tsconfig.json", ctx.fs)) { isValid = true; return ctx; } else { ctx.logger.error("The compiler options has not been adjusted. Please sets `compilerOptions.allowJs` to `true`."); } } return ctx; }; /** * Recursively checks whether allowJs is enabled in the provided tsconfig or any * referenced configuration files. * * @param tsconfigPath The path to the tsconfig to inspect. * @param fs The file system used to read the configs. * @param visited A set of already inspected files to avoid circular lookups. * @example * ```ts * await hasAllowJsEnabled("./tsconfig.json", fs); * ``` */ export const hasAllowJsEnabled = async (tsconfigPath, fs, visited = new Set()) => { const normalizedPath = normalizeConfigPath(tsconfigPath); if (visited.has(normalizedPath)) { return false; } visited.add(normalizedPath); const file = await fs.readFile(normalizedPath, { encoding: "utf-8" }); const tsconfig = JSON5.parse(file); if (tsconfig?.compilerOptions?.allowJs === true) { return true; } const baseDir = nodePath.dirname(normalizedPath); const extendCandidates = Array.isArray(tsconfig?.extends) ? tsconfig.extends : tsconfig?.extends ? [tsconfig.extends] : []; for (const candidate of extendCandidates) { if (typeof candidate !== "string") continue; const resolved = await resolveExtendedConfig(candidate, baseDir, fs); if (resolved && (await hasAllowJsEnabled(resolved, fs, visited))) { return true; } } if (Array.isArray(tsconfig?.references)) { for (const reference of tsconfig.references) { const referencePath = reference?.path; if (typeof referencePath !== "string") continue; const resolved = await resolveReferenceConfig(referencePath, baseDir, fs); if (resolved && (await hasAllowJsEnabled(resolved, fs, visited))) { return true; } } } return false; }; /** * Normalizes a tsconfig path to an absolute path. */ const normalizeConfigPath = (configPath) => { return nodePath.isAbsolute(configPath) ? configPath : nodePath.resolve(process.cwd(), configPath); }; /** * Resolves the extended tsconfig path relative to the base config. */ const resolveExtendedConfig = async (extendsSpecifier, baseDir, fs) => { const candidates = new Set(); const resolvedBase = nodePath.isAbsolute(extendsSpecifier) ? extendsSpecifier : nodePath.resolve(baseDir, extendsSpecifier); candidates.add(resolvedBase); if (nodePath.extname(resolvedBase) === "") { candidates.add(`${resolvedBase}.json`); candidates.add(nodePath.join(resolvedBase, "tsconfig.json")); } for (const candidate of candidates) { if (await pathExists(candidate, fs)) { return candidate; } } try { return require.resolve(extendsSpecifier, { paths: [baseDir] }); } catch { if (extendsSpecifier.endsWith(".json") === false) { try { return require.resolve(`${extendsSpecifier}.json`, { paths: [baseDir], }); } catch { return undefined; } } } return undefined; }; /** * Resolves the tsconfig referenced through the `references` property. */ const resolveReferenceConfig = async (referenceSpecifier, baseDir, fs) => { const candidates = new Set(); const resolvedBase = nodePath.isAbsolute(referenceSpecifier) ? referenceSpecifier : nodePath.resolve(baseDir, referenceSpecifier); candidates.add(resolvedBase); if (nodePath.extname(resolvedBase) === "") { candidates.add(`${resolvedBase}.json`); candidates.add(nodePath.join(resolvedBase, "tsconfig.json")); } for (const candidate of candidates) { if (await pathExists(candidate, fs)) { try { const stats = await fs.stat(candidate); if (stats.isDirectory()) { const directoryConfig = nodePath.join(candidate, "tsconfig.json"); if (await pathExists(directoryConfig, fs)) { return directoryConfig; } continue; } } catch { // ignore, we'll continue checking other candidates } return candidate; } } return undefined; }; // /** // * Ensures that the moduleResolution compiler option is set to "bundler" or similar in the tsconfig.json. // * // * Otherwise, types defined in `package.exports` are not resolved by TypeScript. Leading to type // * errors with Paraglide-JS. // */ // const maybeUpdateTsConfigModuleResolution: CliStep< // { fs: typeof import("node:fs/promises"); logger: Logger }, // unknown // > = async (ctx) => { // if ((await pathExists("./tsconfig.json", ctx.fs)) === false) { // return ctx; // } // const file = await ctx.fs.readFile("./tsconfig.json", { // encoding: "utf-8", // }); // // tsconfig allows comments ... FML // let tsconfig = JSON5.parse(file); // let parentTsConfig: any | undefined; // if (tsconfig.extends) { // try { // const parentTsConfigPath = nodePath.resolve( // process.cwd(), // tsconfig.extends // ); // const parentTsConfigFile = await ctx.fs.readFile(parentTsConfigPath, { // encoding: "utf-8", // }); // parentTsConfig = JSON5.parse(parentTsConfigFile); // } catch { // ctx.logger.warn( // `The tsconfig.json is extended from a tsconfig that couldn't be read. Maybe the file doesn't exist yet or is a NPM package. Continuing without taking the extended from tsconfig into consideration.` // ); // } // } // // options that don't support package.exports // const invalidOptions = ["classic", "node", "node10"]; // const moduleResolution = // tsconfig.compilerOptions?.moduleResolution ?? // parentTsConfig?.compilerOptions?.moduleResolution; // if ( // moduleResolution && // invalidOptions.includes(moduleResolution.toLowerCase()) === false // ) { // // the moduleResolution is already set to bundler or similar // return ctx; // } // ctx.logger.info( // `The \`compilerOptions.moduleResolution\` options must be set to "Bundler" in the \`tsconfig.json\` file: // \`{ // "compilerOptions": { // "moduleResolution": "Bundler" // } // }\`` // ); // let isValid = false; // while (isValid === false) { // const response = await prompt( // `Is \`compilerOptions.moduleResolution\` set to "Bundler"?`, // { // type: "confirm", // initial: true, // } // ); // if (response === false) { // ctx.logger.warn( // "Continuing without adjusting the tsconfig.json. This may lead to type errors." // ); // return ctx; // } // // don't re-ask the question if there is an `extends` present in the tsconfig // // just trust that it's correct. // if (tsconfig.extends) { // isValid = true; // return ctx; // } // const file = await ctx.fs.readFile("./tsconfig.json", { // encoding: "utf-8", // }); // tsconfig = JSON5.parse(file); // if ( // tsconfig?.compilerOptions?.moduleResolution && // tsconfig.compilerOptions.moduleResolution.toLowerCase() === "bundler" // ) { // isValid = true; // return ctx; // } else { // ctx.logger.error( // "The compiler options have not been adjusted. Please set the `compilerOptions.moduleResolution` to `Bundler`." // ); // } // } // return ctx; // }; //# sourceMappingURL=update-ts-config.js.map