Skip to content
Merged
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
2 changes: 1 addition & 1 deletion dist/node/index.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion dist/node/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/node/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ingest } from '../core/ingest.js';
import type { Session } from '../core/DataModel.js';
import type { ISourceNode } from '../core/NodeInterfaces.js';
import { reasonOf, type ParseWarning } from '../datamodel/parser/ParseWarning.js';
import { isTomlProjectFile } from '../datamodel/fileKinds.js';

// Arm the fast inflate engine explicitly. `Inflate.ts` can find `node:zlib` on its
// own through `process.getBuiltinModule`, but that method only exists from Node 22.3,
Expand All @@ -39,6 +40,25 @@ setNativeInflate({

const SUPPORTED = new Set(['.sldd', '.slx', '.mdl', '.mat', '.prj']);

/**
* Whether a directory entry is something `loadFromPath` can open.
*
* An extension set AND a name test, because one supported file is identified by its
* NAME: a TOML-format project's entire definition is a file called `matlab.toml`, with
* no `.prj` marker and no `resources/` left to find it by. Matching `.toml` instead
* would call every `Cargo.toml` and `pyproject.toml` in a folder a MATLAB project —
* see `isTomlProjectFile`, which is why that predicate exists.
*
* It is that shared predicate and deliberately not a literal here, because this is the
* second path over the same rule: `ingest` dispatches a project on `isProjectFile`, so
* a scan that disagrees either hides a file that opens fine or lists one nothing can
* open. Keeping the extensions as a set is still right — they are this module's own
* business, and `ingest` sniffs CONTENT rather than extension for all of them.
*/
function isSupportedName(name: string): boolean {
return SUPPORTED.has(extname(name).toLowerCase()) || isTomlProjectFile(name);
}

// Re-export createSession so Node consumers can import everything from one place.
export const createSession = _createSession;

Expand Down Expand Up @@ -73,7 +93,7 @@ export function loadFromPath(session: Session, path: string): ISourceNode {
* minus the stderr write. Same shape as SlddNode.parse's sink.
*/
export function loadDirectory(session: Session, dir: string, skipped?: ParseWarning[]): ISourceNode[] {
const names = readdirSync(dir).filter((n) => SUPPORTED.has(extname(n).toLowerCase())).sort();
const names = readdirSync(dir).filter(isSupportedName).sort();
const out: ISourceNode[] = [];
for (const name of names) {
try {
Expand Down
27 changes: 27 additions & 0 deletions test/node-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,31 @@ describe('node loader', () => {
expect(loaded.map((n) => n.name)).toEqual(['legacy.MDL', 'upper.SLDD']);
expect(errors).toEqual([]);
});

it('loadDirectory finds a TOML project by NAME, and no other .toml', () => {
// The one supported file an extension set cannot express. A project converted with
// `matlab.project.DefinitionFiles.Toml` has no `.prj` and no `resources/` — its whole
// definition is a file called `matlab.toml` — so a scan filtering on extensions alone
// reads a real project directory as holding nothing, which is how this started.
//
// The marker is MATLAB-WRITTEN, copied from the parity artifact rather than typed
// here: a hand-authored TOML standing in for what MATLAB writes is how a reader comes
// to depend on a spelling MATLAB never uses.
const dir = mkdtempSync(join(tmpdir(), 'dex-toml-dir-'));
copyFileSync(
fileURLToPath(new URL('./parity/artifacts/project/Toml/parityProject/matlab.toml', import.meta.url)),
join(dir, 'matlab.toml'),
);
// The hazard the NAME test exists for, written so its absence below is the filter
// rejecting it and not an empty directory: `.toml` is the config format of half the
// tooling a MATLAB repository sits beside.
writeFileSync(join(dir, 'Cargo.toml'), '[package]\nname = "not-a-matlab-project"\n');
writeFileSync(join(dir, 'pyproject.toml'), '[project]\nname = "also-not"\n');

const skipped: ParseWarning[] = [];
const loaded = loadDirectory(createSession(), dir, skipped);

expect(loaded.map((n) => n.name)).toEqual(['matlab.toml']);
expect(skipped).toEqual([]); // listed AND opened — a listing that then fails is worse
});
});
Loading