Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 143 additions & 1 deletion packages/plugins/apps/src/backend/ast-parsing/module-graph.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,36 @@

import { parseAst } from 'rollup/parseAst';

import { createParsedModuleRecord } from './module-graph';
import {
createParsedModuleRecord,
type ExportBinding,
type ImportBinding,
type ParsedModuleRecord,
type StaticBinding,
} from './module-graph';

const buildRoot = '/project';

function createRecord(code: string, staticDependencies: string[] = []): ParsedModuleRecord {
const record = createParsedModuleRecord(
'/project/src/backend/actions.backend.js',
buildRoot,
parseAst(code),
staticDependencies,
);

if (!record) {
throw new Error('Expected module record to be created');
}
return record;
}

function bindingsByVariableName<T>(bindings: Map<{ name: string }, T>): Record<string, T> {
return Object.fromEntries(
[...bindings.entries()].map(([variable, binding]) => [variable.name, binding]),
);
}

describe('Backend Functions - module graph records', () => {
test('Should create graph records for app-local backend modules', () => {
const record = createParsedModuleRecord(
Expand Down Expand Up @@ -128,4 +154,120 @@ describe('Backend Functions - module graph records', () => {

expect(record?.unsupportedDependencies).toEqual([]);
});

test('Should record import bindings by declared variable identity', () => {
const record = createRecord(
`
import { HTTP_ID as ACTIVE_ID } from './ids.js';
import DEFAULT_ID from './defaults.js';
import * as namespaceIds from './namespace.js';
`,
[
'/project/src/backend/ids.js',
'/project/src/backend/defaults.js',
'/project/src/backend/namespace.js',
],
);

expect(bindingsByVariableName<ImportBinding>(record.importsByVariable)).toMatchObject({
ACTIVE_ID: {
kind: 'named',
importedName: 'HTTP_ID',
resolvedId: '/project/src/backend/ids.js',
},
DEFAULT_ID: {
kind: 'default',
resolvedId: '/project/src/backend/defaults.js',
},
namespaceIds: {
kind: 'namespace',
resolvedId: '/project/src/backend/namespace.js',
},
});
});

test('Should record local exports, re-exports, unsupported named exports, and star exports', () => {
const record = createRecord(
`
const LOCAL_ID = 'conn-local';
const { PATTERN_ID } = ids;

export const DIRECT_ID = 'conn-direct';
export { LOCAL_ID as ACTIVE_ID, PATTERN_ID };
export { REMOTE_ID as FORWARDED_ID, default as DEFAULT_ID } from './ids.js';
export * as namespaceIds from './namespace.js';
export * from './star.js';
export { LOCAL_ID as default };
`,
[
'/project/src/backend/ids.js',
'/project/src/backend/namespace.js',
'/project/src/backend/star.js',
],
);
const exportsByName = Object.fromEntries(record.exportsByName) as Record<
string,
ExportBinding
>;

expect(exportsByName.ACTIVE_ID).toMatchObject({ kind: 'local' });
expect(exportsByName.DIRECT_ID).toMatchObject({ kind: 'local' });
expect(exportsByName.PATTERN_ID).toMatchObject({ kind: 'local' });
expect(exportsByName.FORWARDED_ID).toEqual({
kind: 're-export',
importedName: 'REMOTE_ID',
resolvedId: '/project/src/backend/ids.js',
});
expect(exportsByName.DEFAULT_ID).toEqual({
kind: 're-export',
importedName: 'default',
resolvedId: '/project/src/backend/ids.js',
});
expect(exportsByName.namespaceIds).toEqual({
kind: 'unsupported',
reason: 'namespace re-export',
resolvedId: '/project/src/backend/namespace.js',
});
expect(exportsByName.default).toEqual({
kind: 'unsupported',
reason: 'default export',
});
expect(record.starExports).toEqual([{ resolvedId: '/project/src/backend/star.js' }]);
});

test('Should record top-level static bindings by declared variable identity', () => {
const record = createRecord(`
const CONST_ID = 'conn-const';
let MUTABLE_ID = 'conn-mutable';
const { PATTERN_ID } = ids;
function getId() {
return 'conn-function';
}
export default function defaultGetId() {
return 'conn-default-function';
}
const CONNECTIONS = { HTTP: 'conn-http' };
CONNECTIONS.HTTP = 'conn-mutated';
const DELETED_CONNECTIONS = { HTTP: 'conn-http' };
delete DELETED_CONNECTIONS.HTTP;
const FOR_IN_CONNECTIONS = { HTTP: 'conn-http' };
for (FOR_IN_CONNECTIONS.HTTP in source) {}
const FOR_OF_CONNECTIONS = { HTTP: 'conn-http' };
for (FOR_OF_CONNECTIONS.HTTP of source) {}
`);

expect(
bindingsByVariableName<StaticBinding>(record.topLevelBindingsByVariable),
).toMatchObject({
CONST_ID: { kind: 'const' },
MUTABLE_ID: { kind: 'mutable', declarationKind: 'let' },
PATTERN_ID: { kind: 'unsupported', reason: 'binding pattern' },
getId: { kind: 'unsupported', reason: 'FunctionDeclaration binding' },
defaultGetId: { kind: 'unsupported', reason: 'FunctionDeclaration binding' },
CONNECTIONS: { kind: 'unsupported', reason: 'mutated object binding' },
DELETED_CONNECTIONS: { kind: 'unsupported', reason: 'mutated object binding' },
FOR_IN_CONNECTIONS: { kind: 'unsupported', reason: 'mutated object binding' },
FOR_OF_CONNECTIONS: { kind: 'unsupported', reason: 'mutated object binding' },
});
});
});
Loading
Loading