mirror of
https://github.com/selfxyz/self.git
synced 2026-01-13 16:47:57 -05:00
* chore(app): tighten types across app workspace * fixes * save wip * save wip linting * fix any types * fix types * fix: import forwardRef directly from react - Changed from React.forwardRef to forwardRef import - Resolves linting warning about named exports * save typing and linting updates * cr feedback. final pass * more cr feedback * pipeline fixes
220 lines
7.2 KiB
JavaScript
220 lines
7.2 KiB
JavaScript
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
|
|
|
const path = require('node:path');
|
|
const { Project, SyntaxKind } = require('ts-morph');
|
|
|
|
function determineAliasStrategy(dir, abs, baseDir, baseAlias) {
|
|
const rel = path.relative(baseDir, abs).replace(/\\/g, '/');
|
|
return rel ? `${baseAlias}/${rel}` : baseAlias;
|
|
}
|
|
|
|
function transformProjectToAliasImports(project, appRootPath) {
|
|
const srcDir = path.join(appRootPath, 'src');
|
|
const testsDir = path.join(appRootPath, 'tests', 'src');
|
|
|
|
const sourceFiles = project.getSourceFiles();
|
|
for (const sourceFile of sourceFiles) {
|
|
const dir = path.dirname(sourceFile.getFilePath());
|
|
|
|
// Handle import declarations
|
|
for (const declaration of sourceFile.getImportDeclarations()) {
|
|
try {
|
|
// Skip if no module specifier or not a string literal
|
|
const moduleSpecifier = declaration.getModuleSpecifier();
|
|
if (
|
|
!moduleSpecifier ||
|
|
moduleSpecifier.getKind() !== SyntaxKind.StringLiteral
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const spec = declaration.getModuleSpecifierValue();
|
|
|
|
// Skip existing alias imports
|
|
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
|
continue;
|
|
}
|
|
|
|
// Handle relative imports
|
|
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
|
const abs = path.resolve(dir, spec);
|
|
let baseDir = null;
|
|
let baseAlias = null;
|
|
|
|
// Determine containment safely using path.relative to avoid startsWith false positives
|
|
const relFromSrc = path.relative(srcDir, abs);
|
|
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
|
baseDir = srcDir;
|
|
baseAlias = '@';
|
|
} else {
|
|
const relFromTests = path.relative(testsDir, abs);
|
|
if (
|
|
!relFromTests.startsWith('..') &&
|
|
!path.isAbsolute(relFromTests)
|
|
) {
|
|
baseDir = testsDir;
|
|
baseAlias = '@tests';
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
|
declaration.setModuleSpecifier(newSpec);
|
|
} catch (error) {
|
|
// Skip declarations that can't be processed (e.g., type-only imports with issues)
|
|
const msg = error instanceof Error ? error.message : String(error);
|
|
console.warn(
|
|
`Skipping import declaration in ${sourceFile.getFilePath()}: ${msg}`,
|
|
);
|
|
try {
|
|
console.debug('Import declaration text:', declaration.getText());
|
|
} catch {}
|
|
if (error && typeof error === 'object' && 'stack' in error) {
|
|
console.debug('Error stack:', error.stack);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Handle export declarations like: export * from '../x' or export {A} from '../x'
|
|
for (const declaration of sourceFile.getExportDeclarations()) {
|
|
try {
|
|
// Skip if no module specifier or not a string literal
|
|
const moduleSpecifier = declaration.getModuleSpecifier();
|
|
if (
|
|
!moduleSpecifier ||
|
|
moduleSpecifier.getKind() !== SyntaxKind.StringLiteral
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const spec = declaration.getModuleSpecifierValue();
|
|
if (!spec) continue;
|
|
|
|
// Skip existing alias exports
|
|
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
|
continue;
|
|
}
|
|
|
|
// Handle relative exports
|
|
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
|
const abs = path.resolve(dir, spec);
|
|
let baseDir = null;
|
|
let baseAlias = null;
|
|
|
|
const relFromSrc = path.relative(srcDir, abs);
|
|
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
|
baseDir = srcDir;
|
|
baseAlias = '@';
|
|
} else {
|
|
const relFromTests = path.relative(testsDir, abs);
|
|
if (
|
|
!relFromTests.startsWith('..') &&
|
|
!path.isAbsolute(relFromTests)
|
|
) {
|
|
baseDir = testsDir;
|
|
baseAlias = '@tests';
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
|
declaration.setModuleSpecifier(newSpec);
|
|
} catch (error) {
|
|
// Skip declarations that can't be processed
|
|
const msg = error instanceof Error ? error.message : String(error);
|
|
console.warn(
|
|
`Skipping export declaration in ${sourceFile.getFilePath()}: ${msg}`,
|
|
);
|
|
try {
|
|
console.debug('Export declaration text:', declaration.getText());
|
|
} catch {}
|
|
if (error && typeof error === 'object' && 'stack' in error) {
|
|
console.debug('Error stack:', error.stack);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Handle require() calls
|
|
const requireCalls = sourceFile.getDescendantsOfKind(
|
|
SyntaxKind.CallExpression,
|
|
);
|
|
for (const call of requireCalls) {
|
|
const expression = call.getExpression();
|
|
const exprText = expression.getText();
|
|
const isRequire = exprText === 'require';
|
|
const isDynamicImport = exprText === 'import';
|
|
const isJestMock =
|
|
exprText === 'jest.mock' ||
|
|
exprText === 'jest.doMock' ||
|
|
exprText === 'jest.unmock';
|
|
if (!isRequire && !isDynamicImport && !isJestMock) continue;
|
|
|
|
const args = call.getArguments();
|
|
if (args.length === 0) continue;
|
|
|
|
const arg = args[0];
|
|
if (arg.getKind() !== SyntaxKind.StringLiteral) continue;
|
|
|
|
const spec = arg.getLiteralValue();
|
|
|
|
// Skip existing alias requires
|
|
if (spec.startsWith('@/') || spec.startsWith('@tests/')) {
|
|
continue;
|
|
}
|
|
|
|
// Handle relative requires
|
|
if (!spec.startsWith('./') && !spec.startsWith('../')) continue;
|
|
|
|
const abs = path.resolve(dir, spec);
|
|
let baseDir = null;
|
|
let baseAlias = null;
|
|
|
|
// Determine containment safely using path.relative to avoid startsWith false positives
|
|
const relFromSrc = path.relative(srcDir, abs);
|
|
if (!relFromSrc.startsWith('..') && !path.isAbsolute(relFromSrc)) {
|
|
baseDir = srcDir;
|
|
baseAlias = '@';
|
|
} else {
|
|
const relFromTests = path.relative(testsDir, abs);
|
|
if (!relFromTests.startsWith('..') && !path.isAbsolute(relFromTests)) {
|
|
baseDir = testsDir;
|
|
baseAlias = '@tests';
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const newSpec = determineAliasStrategy(dir, abs, baseDir, baseAlias);
|
|
arg.setLiteralValue(newSpec);
|
|
}
|
|
}
|
|
}
|
|
|
|
function runAliasImportsTransform(options = {}) {
|
|
const appRoot = options.appRoot || path.resolve(__dirname, '..');
|
|
const project =
|
|
options.project ||
|
|
new Project({ tsConfigFilePath: path.join(appRoot, 'tsconfig.json') });
|
|
|
|
// Include test files since they're excluded in tsconfig
|
|
if (!options.skipAddTests) {
|
|
project.addSourceFilesAtPaths(['tests/**/*.{ts,tsx}']);
|
|
}
|
|
|
|
transformProjectToAliasImports(project, appRoot);
|
|
project.saveSync();
|
|
return project;
|
|
}
|
|
|
|
if (require.main === module) {
|
|
runAliasImportsTransform();
|
|
}
|
|
|
|
module.exports = { runAliasImportsTransform, transformProjectToAliasImports };
|