-
Notifications
You must be signed in to change notification settings - Fork 5.5k
feat: import aliases #40695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
n3ps
wants to merge
4
commits into
main
Choose a base branch
from
n3ps/import-alias
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+308
−73
Open
feat: import aliases #40695
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
5a2856e
feat: add ~/ui and ~/shared import alias infrastructure
cursoragent 3c8b521
feat: add ~/ui and ~/shared import alias infrastructure
cursoragent 14be0c3
refactor: use ~/shared and ~/ui aliases in activity-v2
cursoragent 33c12e0
vscode settings
n3ps File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| const path = require('path'); | ||
|
Check warning on line 1 in development/build/transforms/import-alias.js
|
||
|
|
||
| const ROOT = path.resolve(__dirname, '../../..'); | ||
|
|
||
| /** | ||
| * Mapping of import alias prefixes to directories (relative to project root). | ||
| * Add new aliases here to make them available across the codebase. | ||
| */ | ||
| const ALIASES = { | ||
| '~/ui': 'ui', | ||
| '~/shared': 'shared', | ||
| }; | ||
|
|
||
| /** | ||
| * If `importSource` starts with a known alias, return the equivalent | ||
| * relative path from `filename`'s directory. Otherwise return null. | ||
| * | ||
| * @param {string} importSource - e.g. '~/shared/constants/network' | ||
| * @param {string} filename - absolute path of the file being compiled | ||
| * @returns {string | null} rewritten relative path or null | ||
| */ | ||
| function rewriteAlias(importSource, filename) { | ||
| for (const [alias, directory] of Object.entries(ALIASES)) { | ||
| if (importSource !== alias && !importSource.startsWith(`${alias}/`)) { | ||
| continue; | ||
| } | ||
|
|
||
| const rest = importSource.slice(alias.length); | ||
| const absoluteTarget = path.join(ROOT, directory, rest); | ||
| let relativePath = path | ||
| .relative(path.dirname(filename), absoluteTarget) | ||
| .split(path.sep) | ||
| .join('/'); | ||
|
|
||
| if (!relativePath.startsWith('.')) { | ||
| relativePath = `./${relativePath}`; | ||
| } | ||
|
|
||
| return relativePath; | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Babel plugin that rewrites `~/ui/...` and `~/shared/...` import aliases | ||
| * to relative paths. This allows browserify (which has no native alias | ||
| * support) to resolve them using standard Node module resolution. | ||
| * | ||
| * @returns {import('@babel/core').PluginObj} Babel plugin object | ||
| */ | ||
| module.exports = function importAliasPlugin() { | ||
| return { | ||
| visitor: { | ||
| // import X from '~/shared/...' | ||
| // export { X } from '~/shared/...' | ||
| // export * from '~/shared/...' | ||
| 'ImportDeclaration|ExportNamedDeclaration|ExportAllDeclaration'( | ||
| nodePath, | ||
| state, | ||
| ) { | ||
| const { source } = nodePath.node; | ||
| if (!source) { | ||
| return; | ||
| } | ||
|
|
||
| const rewritten = rewriteAlias(source.value, state.filename); | ||
| if (rewritten) { | ||
| source.value = rewritten; | ||
| } | ||
| }, | ||
|
|
||
| // require('~/shared/...') | ||
| CallExpression(nodePath, state) { | ||
| const { callee } = nodePath.node; | ||
| const arg = nodePath.node.arguments[0]; | ||
|
|
||
| if ( | ||
| callee.type !== 'Identifier' || | ||
| callee.name !== 'require' || | ||
| !arg || | ||
| arg.type !== 'StringLiteral' | ||
|
Check warning on line 82 in development/build/transforms/import-alias.js
|
||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const rewritten = rewriteAlias(arg.value, state.filename); | ||
| if (rewritten) { | ||
| arg.value = rewritten; | ||
| } | ||
| }, | ||
| }, | ||
| }; | ||
| }; | ||
|
|
||
| module.exports.rewriteAlias = rewriteAlias; | ||
| module.exports.ALIASES = ALIASES; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| const path = require('path'); | ||
| const { transformSync } = require('@babel/core'); | ||
| const { rewriteAlias } = require('./import-alias'); | ||
|
|
||
| const ROOT = path.resolve(__dirname, '../../..'); | ||
|
|
||
| describe('import-alias babel plugin', () => { | ||
| function transform(code, filePath) { | ||
| const result = transformSync(code, { | ||
| filename: path.join(ROOT, filePath), | ||
| plugins: [require.resolve('./import-alias')], | ||
| parserOpts: { plugins: ['typescript'] }, | ||
| configFile: false, | ||
| babelrc: false, | ||
| }); | ||
| return result.code; | ||
| } | ||
|
|
||
| describe('rewriteAlias', () => { | ||
| it('rewrites ~/shared/ to a relative path', () => { | ||
| const filename = path.join(ROOT, 'app/scripts/migrations/183.ts'); | ||
| const result = rewriteAlias('~/shared/constants/network', filename); | ||
| expect(result).toBe('../../../shared/constants/network'); | ||
| }); | ||
|
|
||
| it('rewrites ~/ui/ to a relative path', () => { | ||
| const filename = path.join( | ||
| ROOT, | ||
| 'ui/components/multichain/activity-v2/hooks.ts', | ||
| ); | ||
| const result = rewriteAlias('~/ui/hooks/useI18nContext', filename); | ||
| expect(result).toBe('../../../hooks/useI18nContext'); | ||
| }); | ||
|
|
||
| it('returns null for non-alias imports', () => { | ||
| const filename = path.join(ROOT, 'ui/components/foo.ts'); | ||
| expect(rewriteAlias('./helpers', filename)).toBeNull(); | ||
| expect(rewriteAlias('react', filename)).toBeNull(); | ||
| expect(rewriteAlias('@metamask/utils', filename)).toBeNull(); | ||
| }); | ||
|
|
||
| it('does not match partial prefix like ~/shared-extra', () => { | ||
| const filename = path.join(ROOT, 'ui/components/foo.ts'); | ||
| expect(rewriteAlias('~/shared-extra/foo', filename)).toBeNull(); | ||
| }); | ||
|
|
||
| it('handles bare alias without subpath', () => { | ||
| const filename = path.join(ROOT, 'app/scripts/background.js'); | ||
| const result = rewriteAlias('~/shared', filename); | ||
| expect(result).toBe('../../shared'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('babel transform: import declarations', () => { | ||
| it('rewrites named import from ~/shared/', () => { | ||
| const code = `import { CHAIN_IDS } from '~/shared/constants/network';`; | ||
| const output = transform( | ||
| code, | ||
| 'ui/components/multichain/activity-v2/hooks.ts', | ||
| ); | ||
| expect(output).toContain(`from "../../../../shared/constants/network"`); | ||
| expect(output).not.toContain('~/'); | ||
| }); | ||
|
|
||
| it('rewrites default import from ~/ui/', () => { | ||
| const code = `import AssetPage from '~/ui/pages/asset/components/asset-page';`; | ||
| const output = transform(code, 'ui/components/multichain/foo.ts'); | ||
| expect(output).toContain( | ||
| `from "../../pages/asset/components/asset-page"`, | ||
| ); | ||
| expect(output).not.toContain('~/'); | ||
| }); | ||
|
|
||
| it('rewrites type import from ~/shared/', () => { | ||
| const code = `import type { Token } from '~/shared/lib/multichain/types';`; | ||
| const output = transform( | ||
| code, | ||
| 'ui/components/multichain/activity-v2/hooks.ts', | ||
| ); | ||
| expect(output).toContain(`"../../../../shared/lib/multichain/types"`); | ||
| }); | ||
|
|
||
| it('does not touch non-alias imports', () => { | ||
| const code = [ | ||
| `import React from 'react';`, | ||
| `import { Box } from '@metamask/design-system-react';`, | ||
| `import { foo } from './helpers';`, | ||
| `import { bar } from '../utils';`, | ||
| ].join('\n'); | ||
| const output = transform(code, 'ui/components/foo.ts'); | ||
| expect(output).toContain(`react`); | ||
| expect(output).toContain(`@metamask/design-system-react`); | ||
| expect(output).toContain(`./helpers`); | ||
| expect(output).toContain(`../utils`); | ||
| expect(output).not.toContain('~/'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('babel transform: export declarations', () => { | ||
| it('rewrites export { X } from ~/shared/', () => { | ||
| const code = `export { CHAIN_IDS } from '~/shared/constants/network';`; | ||
| const output = transform(code, 'app/scripts/lib/util.ts'); | ||
| expect(output).toContain(`from "../../../shared/constants/network"`); | ||
| }); | ||
|
|
||
| it('rewrites export * from ~/ui/', () => { | ||
| const code = `export * from '~/ui/selectors';`; | ||
| const output = transform(code, 'ui/components/foo.ts'); | ||
| expect(output).toContain(`from "../selectors"`); | ||
| }); | ||
| }); | ||
|
|
||
| describe('babel transform: require calls', () => { | ||
| it('rewrites require(~/shared/)', () => { | ||
| const code = `const { foo } = require('~/shared/lib/sentry');`; | ||
| const output = transform(code, 'app/scripts/migrations/183.ts'); | ||
| expect(output).toContain(`require("../../../shared/lib/sentry")`); | ||
| }); | ||
|
|
||
| it('does not touch non-alias require calls', () => { | ||
| const code = `const path = require('path');`; | ||
| const output = transform(code, 'app/scripts/lib/util.ts'); | ||
| expect(output).toContain(`require('path')`); | ||
| }); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalid VS Code settings replace valid ones silently
Medium Severity
js/ts.preferences.importModuleSpecifierandjs/ts.tsdk.pathare not valid VS Code setting keys. VS Code uses separate namespaces:javascript.preferences.importModuleSpecifier,typescript.preferences.importModuleSpecifier, andtypescript.tsdk. The removed settings were correct; the replacements will be silently ignored, so the intended "non-relative" import preference (the core DX goal of this PR) won't take effect, and the TypeScript SDK path is lost.