Skip to content

Commit 0f20060

Browse files
committed
Update
1 parent d03d984 commit 0f20060

2 files changed

Lines changed: 99 additions & 3 deletions

File tree

eslint-rules/no-barrel-imports.js

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,12 @@ function getModuleExports(filepath, stack = new Set()) {
9191
return null;
9292
}
9393

94+
const localImports = collectLocalImports(ast.body);
95+
9496
const exports = new Map();
9597
for (const stmt of ast.body) {
96-
if (stmt.type === 'ExportNamedDeclaration') collectNamedExports(stmt, exports, filepath, stack);
98+
if (stmt.type === 'ExportNamedDeclaration')
99+
collectNamedExports(stmt, exports, filepath, stack, localImports);
97100
else if (stmt.type === 'ExportAllDeclaration')
98101
collectStarExports(stmt, exports, filepath, stack);
99102
else if (stmt.type === 'ExportDefaultDeclaration') {
@@ -111,10 +114,91 @@ function getModuleExports(filepath, stack = new Set()) {
111114
return exports;
112115
}
113116

114-
function collectNamedExports(stmt, exports, filepath, stack) {
117+
// Collect value-level named/default imports so that `export const X = Y`
118+
// can be resolved as a re-export of Y's original source. We skip type-only
119+
// imports (they can't legally appear in a value-position initializer).
120+
function collectLocalImports(body) {
121+
const map = new Map();
122+
for (const stmt of body) {
123+
if (stmt.type !== 'ImportDeclaration') continue;
124+
if (stmt.importKind === 'type') continue;
125+
const sourceSpec = stmt.source.value;
126+
for (const spec of stmt.specifiers) {
127+
if (spec.importKind === 'type') continue;
128+
if (spec.type === 'ImportSpecifier') {
129+
map.set(spec.local.name, { sourceSpec, importedName: idOrStr(spec.imported) });
130+
} else if (spec.type === 'ImportDefaultSpecifier') {
131+
map.set(spec.local.name, { sourceSpec, importedName: 'default' });
132+
}
133+
}
134+
}
135+
return map;
136+
}
137+
138+
// Strip TS `as`/type-assertion wrappers from an initializer and return the
139+
// inner identifier name if the whole expression is just `Ident (as T)*`.
140+
function unwrapAliasInit(node) {
141+
let cur = node;
142+
while (cur && (cur.type === 'TSAsExpression' || cur.type === 'TSTypeAssertion')) {
143+
cur = cur.expression;
144+
}
145+
return cur && cur.type === 'Identifier' ? cur.name : null;
146+
}
147+
148+
function buildAliasExport(importEntry, fromFile, stack, stmtIsType) {
149+
const { sourceSpec, importedName } = importEntry;
150+
const targetFile = resolveImportSource(sourceSpec, fromFile);
151+
const isBare = !sourceSpec.startsWith('.');
152+
let source = targetFile;
153+
let bareSource = !targetFile && isBare ? sourceSpec : null;
154+
let local = importedName;
155+
156+
if (targetFile) {
157+
const nested = getModuleExports(targetFile, stack);
158+
const nestedEntry = nested?.get(importedName);
159+
if (nestedEntry && nestedEntry.kind !== 'namespace') {
160+
if (nestedEntry.source) {
161+
source = nestedEntry.source;
162+
bareSource = null;
163+
local = nestedEntry.localName;
164+
} else if (nestedEntry.bareSource) {
165+
source = null;
166+
bareSource = nestedEntry.bareSource;
167+
local = nestedEntry.localName;
168+
}
169+
}
170+
}
171+
172+
return { source, bareSource, localName: local, isType: stmtIsType, kind: 'named' };
173+
}
174+
175+
function collectNamedExports(stmt, exports, filepath, stack, localImports) {
115176
const stmtIsType = stmt.exportKind === 'type';
116177

117178
if (stmt.declaration) {
179+
// `export const X = Y [as T]` where Y is a top-level imported binding is
180+
// treated as a re-export of Y's original source. This intentionally drops
181+
// any branding/cast type — internal call sites don't need it, and it lets
182+
// consumers import the implementation directly for tree-shaking.
183+
if (stmt.declaration.type === 'VariableDeclaration') {
184+
for (const v of stmt.declaration.declarations) {
185+
if (v.id?.type !== 'Identifier') continue;
186+
const name = v.id.name;
187+
const aliased = v.init ? unwrapAliasInit(v.init) : null;
188+
const importEntry = aliased ? localImports.get(aliased) : null;
189+
if (importEntry) {
190+
setExport(exports, name, buildAliasExport(importEntry, filepath, stack, stmtIsType));
191+
} else {
192+
setExport(exports, name, {
193+
source: filepath,
194+
localName: name,
195+
isType: stmtIsType,
196+
kind: 'local',
197+
});
198+
}
199+
}
200+
return;
201+
}
118202
for (const { name, isType } of declarationNames(stmt.declaration)) {
119203
setExport(exports, name, {
120204
source: filepath,

packages/@ember/template-compiler/lib/compile-options.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
import { and, array, element, eq, fn, hash, neq, not, lt, lte, gt, gte, or } from '@ember/helper';
1+
import { and } from '@glimmer/runtime/lib/helpers/and';
2+
import { array } from '@glimmer/runtime/lib/helpers/array';
3+
import element from '@ember/-internals/glimmer/lib/helpers/element';
4+
import { eq } from '@glimmer/runtime/lib/helpers/eq';
5+
import { fn } from '@glimmer/runtime/lib/helpers/fn';
6+
import { hash } from '@glimmer/runtime/lib/helpers/hash';
7+
import { neq } from '@glimmer/runtime/lib/helpers/neq';
8+
import { not } from '@glimmer/runtime/lib/helpers/not';
9+
import { lt } from '@glimmer/runtime/lib/helpers/lt';
10+
import { lte } from '@glimmer/runtime/lib/helpers/lte';
11+
import { gt } from '@glimmer/runtime/lib/helpers/gt';
12+
import { gte } from '@glimmer/runtime/lib/helpers/gte';
13+
import { or } from '@glimmer/runtime/lib/helpers/or';
214
import { on } from '@ember/modifier/on';
315
import { assert } from '@ember/debug';
416
import {

0 commit comments

Comments
 (0)