Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@travetto/manifest

Package Overview
Dependencies
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@travetto/manifest - npm Package Compare versions

Comparing version 3.0.0-rc.6 to 3.0.0-rc.7

bin/context.d.ts

7

__index__.ts

@@ -5,7 +5,8 @@ /// <reference path="./src/typings.d.ts" />

export * from './src/module';
export * from './src/delta';
export * from './src/manifest-index';
export * from './src/root-index';
export * from './src/delta';
export * from './src/package';
export * from './src/manifest';
export * from './src/types';
export * from './src/util';
export * from './src/types';
export * from './src/watch';
{
"name": "@travetto/manifest",
"version": "3.0.0-rc.6",
"version": "3.0.0-rc.7",
"description": "Manifest support",

@@ -18,4 +18,6 @@ "keywords": [

},
"type": "module",
"files": [
"__index__.ts",
"bin",
"src",

@@ -32,3 +34,10 @@ "support"

},
"dependencies": {},
"peerDependencies": {
"@parcel/watcher": "^2.1.0"
},
"peerDependenciesMeta": {
"@parcel/watcher": {
"optional": true
}
},
"travetto": {

@@ -35,0 +44,0 @@ "displayName": "Manifest",

@@ -1,10 +0,17 @@

import { type Stats } from 'fs';
import fs from 'fs/promises';
import path from 'path';
import {
ManifestDelta, ManifestDeltaEvent, ManifestDeltaModule,
ManifestModule, ManifestModuleFile, ManifestModuleFolderType,
ManifestRoot
ManifestContext, ManifestModule, ManifestModuleCore, ManifestModuleFile,
ManifestModuleFileType, ManifestModuleFolderType, ManifestRoot
} from './types';
import { ManifestModuleUtil } from './module';
type DeltaEventType = 'added' | 'changed' | 'removed' | 'missing' | 'dirty';
type DeltaModule = ManifestModuleCore & { files: Record<string, ManifestModuleFile> };
export type DeltaEvent = { file: string, type: DeltaEventType, module: string };
const VALID_SOURCE_FOLDERS = new Set<ManifestModuleFolderType>(['bin', 'src', 'test', 'support', '$index', '$package', 'doc']);
const VALID_SOURCE_TYPE = new Set<ManifestModuleFileType>(['js', 'ts', 'package-json']);

@@ -23,46 +30,33 @@ /**

*/
static async #deltaModules(outputFolder: string, left: ManifestDeltaModule, right: ManifestDeltaModule): Promise<ManifestDeltaEvent[]> {
const out: ManifestDeltaEvent[] = [];
const getStat = (f: string): Promise<Stats | void> =>
fs.stat(`${outputFolder}/${left.output}/${f.replace(/[.]ts$/, '.js')}`).catch(() => { });
static async #deltaModules(ctx: ManifestContext, outputFolder: string, left: DeltaModule): Promise<DeltaEvent[]> {
const out: DeltaEvent[] = [];
const add = (file: string, type: ManifestDeltaEvent['type']): unknown =>
const add = (file: string, type: DeltaEvent['type']): unknown =>
out.push({ file, module: left.name, type });
const root = `${ctx.workspacePath}/${ctx.outputFolder}/${left.outputFolder}`;
const right = new Set(
(await ManifestModuleUtil.scanFolder(root, left.main))
.filter(x => (x.endsWith('.ts') || x.endsWith('.js') || x.endsWith('package.json')))
.map(x => x.replace(`${root}/`, '').replace(/[.][tj]s$/, ''))
);
for (const el of Object.keys(left.files)) {
if (!(el in right.files)) {
const [, , leftTs] = left.files[el];
const stat = await getStat(el);
if (stat && leftTs < this.#getNewest(stat)) {
// If file pre-exists manifest, be cool
continue;
}
const output = `${outputFolder}/${left.outputFolder}/${el.replace(/[.]ts$/, '.js')}`;
const [, , leftTs] = left.files[el];
const stat = await fs.stat(output).catch(() => { });
right.delete(el.replace(/[.][tj]s$/, ''));
if (!stat) {
add(el, 'added');
} else {
const [, , leftTs] = left.files[el];
const [, , rightTs] = right.files[el];
if (leftTs !== rightTs) {
const stat = await getStat(el);
if (!stat) {
add(el, 'missing');
} else if (leftTs > this.#getNewest(stat!)) {
add(el, 'changed');
}
} else {
const stat = await getStat(el);
if (!stat) {
add(el, 'missing');
} else if (this.#getNewest(stat!) < leftTs) {
add(el, 'dirty');
}
const rightTs = this.#getNewest(stat);
if (leftTs > rightTs) {
add(el, 'changed');
}
}
}
for (const el of Object.keys(right.files)) {
if (!(el in left.files)) {
const stat = await getStat(el);
if (stat) {
add(el, 'removed');
}
}
// Deleted
for (const el of right) {
add(el, 'removed');
}

@@ -85,3 +79,3 @@ return out;

for (const [name, type, date] of m.files?.[key] ?? []) {
if (type === 'ts' || type === 'js' || type === 'package-json') {
if (VALID_SOURCE_TYPE.has(type)) {
out[name] = [name, type, date];

@@ -95,19 +89,15 @@ }

/**
* Produce delta between ttwo manifest roots, relative to a single output folder
* Produce delta between manifest root and the output folder
*/
static async produceDelta(outputFolder: string, left: ManifestRoot, right: ManifestRoot): Promise<ManifestDelta> {
static async produceDelta(ctx: ManifestContext, manifest: ManifestRoot): Promise<DeltaEvent[]> {
const deltaLeft = Object.fromEntries(
Object.values(left.modules)
Object.values(manifest.modules)
.map(m => [m.name, { ...m, files: this.#flattenModuleFiles(m) }])
);
const deltaRight = Object.fromEntries(
Object.values(right.modules)
.map(m => [m.name, { ...m, files: this.#flattenModuleFiles(m) }])
);
const out: DeltaEvent[] = [];
const outputFolder = path.resolve(ctx.workspacePath, ctx.outputFolder);
const out: Record<string, ManifestDeltaEvent[]> = {};
for (const [name, lMod] of Object.entries(deltaLeft)) {
out[name] = await this.#deltaModules(outputFolder, lMod, deltaRight[name] ?? { files: {}, name });
for (const lMod of Object.values(deltaLeft)) {
out.push(...await this.#deltaModules(manifest, outputFolder, lMod));
}

@@ -114,0 +104,0 @@

@@ -52,3 +52,3 @@ import { PackageUtil } from './package';

const workspacePkg = PackageUtil.readPackage(this.ctx.workspacePath);
const workspaceModules = pkg.workspaces?.length ? (await PackageUtil.resolveWorkspaces(req.sourcePath)) : [];
const workspaceModules = pkg.workspaces?.length ? (await PackageUtil.resolveWorkspaces(this.ctx, req.sourcePath)) : [];

@@ -55,0 +55,0 @@ this.#mainPatterns = ModuleDependencyVisitor.getMainPatterns(pkg.name, [

@@ -1,3 +0,1 @@

import fs from 'fs';
import { path } from './path';

@@ -10,2 +8,4 @@

import { ManifestUtil } from './util';
type ScanTest = ((full: string) => boolean) | { test: (full: string) => boolean };

@@ -24,5 +24,5 @@ export type FindConfig = {

module: string;
source: string;
output: string;
relative: string;
sourceFile: string;
outputFile: string;
relativeFile: string;
profile: ManifestProfile;

@@ -33,4 +33,5 @@ type: ManifestModuleFileType;

export type IndexedModule = ManifestModuleCore & {
sourcePath: string;
outputPath: string;
files: Record<ManifestModuleFolderType, IndexedFile[]>;
workspaceRelative: string;
};

@@ -48,3 +49,3 @@

#modulesByFolder: Record<string, IndexedModule> = {};
#root: string;
#outputRoot: string;
#outputToEntry = new Map<string, IndexedFile>();

@@ -54,8 +55,8 @@ #sourceToEntry = new Map<string, IndexedFile>();

constructor(root: string, manifest: string | ManifestRoot) {
this.init(root, manifest);
constructor(manifest: string) {
this.init(manifest);
}
#resolveOutput(...parts: string[]): string {
return path.resolve(this.#root, ...parts);
return path.resolve(this.#outputRoot, ...parts);
}

@@ -67,4 +68,4 @@

get root(): string {
return this.#root;
get outputRoot(): string {
return this.#outputRoot;
}

@@ -76,6 +77,7 @@

init(root: string, manifestInput: string | ManifestRoot): void {
this.#root = root;
this.#manifestFile = typeof manifestInput === 'string' ? manifestInput : manifestInput.manifestFile;
this.#manifest = typeof manifestInput === 'string' ? JSON.parse(fs.readFileSync(this.#manifestFile, 'utf8')) : manifestInput;
init(manifestInput: string): void {
const { manifest, file } = ManifestUtil.readManifestSync(manifestInput);
this.#manifest = manifest;
this.#manifestFile = file;
this.#outputRoot = path.resolve(this.#manifest.workspacePath, this.#manifest.outputFolder);
this.#index();

@@ -86,5 +88,5 @@ }

return files.map(([f, type, ts, profile = 'std']) => {
const source = path.join(m.source, f);
const sourceFile = path.resolve(this.#manifest.workspacePath, m.sourceFolder, f);
const js = (type === 'ts' ? f.replace(/[.]ts$/, '.js') : f);
const output = this.#resolveOutput(m.output, js);
const outputFile = this.#resolveOutput(m.outputFolder, js);
const modImport = `${m.name}/${js}`;

@@ -96,3 +98,3 @@ let id = modImport.replace(`${m.name}/`, _ => _.replace(/[/]$/, ':'));

return { id, type, source, output, import: modImport, profile, relative: f, module: m.name };
return { id, type, sourceFile, outputFile, import: modImport, profile, relativeFile: f, module: m.name };
});

@@ -112,4 +114,4 @@ }

...m,
output: this.#resolveOutput(m.output),
workspaceRelative: m.source.replace(`${this.#manifest.workspacePath}/`, ''),
outputPath: this.#resolveOutput(m.outputFolder),
sourcePath: path.resolve(this.manifest.workspacePath, m.sourceFolder),
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions

@@ -124,6 +126,7 @@ files: Object.fromEntries(

for (const entry of files) {
this.#outputToEntry.set(entry.output, entry);
this.#sourceToEntry.set(entry.source, entry);
this.#outputToEntry.set(entry.outputFile, entry);
this.#sourceToEntry.set(entry.sourceFile, entry);
this.#importToEntry.set(entry.import, entry);
this.#importToEntry.set(entry.import.replace(/[.]js$/, ''), entry);
this.#importToEntry.set(entry.import.replace(/[.]js$/, '.ts'), entry);
}

@@ -133,3 +136,3 @@ }

this.#modulesByName = Object.fromEntries(this.#modules.map(x => [x.name, x]));
this.#modulesByFolder = Object.fromEntries(this.#modules.map(x => [x.workspaceRelative, x]));
this.#modulesByFolder = Object.fromEntries(this.#modules.map(x => [x.sourceFolder, x]));
}

@@ -181,3 +184,3 @@

.filter(({ type }) => type === 'ts')
.filter(({ source }) => filter?.(source) ?? true);
.filter(({ sourceFile: source }) => filter?.(source) ?? true);
}

@@ -234,4 +237,3 @@

resolveFileImport(name: string): string {
name = !name.endsWith('.d.ts') ? name.replace(/[.]ts$/, '.js') : name;
return this.#importToEntry.get(name)?.output ?? name;
return this.#importToEntry.get(name)?.outputFile ?? name;
}

@@ -265,2 +267,10 @@

/**
* Get module from import name
* @param importName
*/
getModuleFromImport(importName: string): IndexedModule | undefined {
const name = this.getFromImport(importName)?.module;
return name ? this.getModule(name) : undefined;
}
/**
* Build module list from an expression list (e.g. `@travetto/app,-@travetto/log)

@@ -267,0 +277,0 @@ */

@@ -32,2 +32,4 @@ import fs from 'fs/promises';

static #scanCache: Record<string, string[]> = {};
static #getNewest(stat: { mtimeMs: number, ctimeMs: number }): number {

@@ -40,4 +42,15 @@ return Math.max(stat.mtimeMs, stat.ctimeMs);

*/
static async #scanFolder(folder: string, topFolders = new Set<string>(), topFiles = new Set<string>()): Promise<string[]> {
static async scanFolder(folder: string, mainSource = false): Promise<string[]> {
if (!mainSource && folder in this.#scanCache) {
return this.#scanCache[folder];
}
if (!await fs.stat(folder).catch(() => false)) {
return [];
}
const topFolders = new Set(mainSource ? [] : ['src', 'bin', 'support']);
const topFiles = new Set(mainSource ? [] : [...INDEX_FILES, 'package.json']);
const out: string[] = [];
if (!fs.stat(folder).catch(() => false)) {

@@ -73,2 +86,7 @@ return out;

}
if (!mainSource) {
this.#scanCache[folder] = out;
}
return out;

@@ -124,2 +142,4 @@ }

return 'support/resources';
} else if (moduleFile.startsWith('support/transform')) {
return '$transformer';
}

@@ -159,9 +179,8 @@ const key = moduleFile.substring(0, folderLocation);

*/
static async describeModule(dep: ModuleDep): Promise<ManifestModule> {
static async describeModule(ctx: ManifestContext, dep: ModuleDep): Promise<ManifestModule> {
const { main, mainSource, local, name, version, sourcePath, profileSet, parentSet, internal } = dep;
const files: ManifestModule['files'] = {};
const folderSet = new Set<ManifestModuleFolderType>(mainSource ? [] : ['src', 'bin', 'support']);
const fileSet = new Set(mainSource ? [] : [...INDEX_FILES, 'package.json']);
for (const file of await this.#scanFolder(sourcePath, folderSet, fileSet)) {
for (const file of await this.scanFolder(sourcePath, mainSource)) {
// Group by top folder

@@ -181,5 +200,7 @@ const moduleFile = file.replace(`${sourcePath}/`, '');

const parents = [...parentSet].sort();
const output = `node_modules/${name}`;
const outputFolder = `node_modules/${name}`;
const sourceFolder = sourcePath === ctx.workspacePath ? '' : sourcePath.replace(`${ctx.workspacePath}/`, '');
return { main, name, version, local, internal, source: sourcePath, output, files, profiles, parents, };
const res = { main, name, version, local, internal, sourceFolder, outputFolder, files, profiles, parents, };
return res;
}

@@ -192,8 +213,8 @@

const visitor = new ModuleDependencyVisitor(ctx);
const declared = await PackageUtil.visitPackages(ctx.mainPath, visitor);
const mainPath = path.resolve(ctx.workspacePath, ctx.mainFolder);
const declared = await PackageUtil.visitPackages(mainPath, visitor);
const sorted = [...declared].sort((a, b) => a.name.localeCompare(b.name));
const modules = await Promise.all(sorted.map(x => this.describeModule(x)));
const modules = await Promise.all(sorted.map(x => this.describeModule(ctx, x)));
return Object.fromEntries(modules.map(m => [m.name, m]));
}
}

@@ -6,3 +6,3 @@ import { readFileSync } from 'fs';

import { Package, PackageDigest, PackageRel, PackageVisitor, PackageVisitReq, PackageWorkspaceEntry } from './types';
import { ManifestContext, Package, PackageDigest, PackageRel, PackageVisitor, PackageVisitReq, PackageWorkspaceEntry } from './types';
import { path } from './path';

@@ -12,3 +12,3 @@

static #req = createRequire(`${path.cwd()}/node_modules`);
static #req = createRequire(path.resolve('node_modules'));
static #framework: Package;

@@ -169,7 +169,14 @@ static #cache: Record<string, Package> = {};

*/
static resolveWorkspaces(rootPath: string): PackageWorkspaceEntry[] {
static async resolveWorkspaces(ctx: ManifestContext, rootPath: string): Promise<PackageWorkspaceEntry[]> {
if (!this.#workspaces[rootPath]) {
const text = execSync('npm query .workspace', { cwd: rootPath, encoding: 'utf8', env: {} });
const res: { location: string, name: string }[] = JSON.parse(text);
this.#workspaces[rootPath] = res.map(d => ({ sourcePath: d.location, name: d.name }));
await fs.mkdir(path.resolve(ctx.workspacePath, ctx.outputFolder), { recursive: true });
const cache = path.resolve(ctx.workspacePath, ctx.outputFolder, 'workspaces.json');
try {
return JSON.parse(await fs.readFile(cache, 'utf8'));
} catch {
const text = execSync('npm query .workspace', { cwd: rootPath, encoding: 'utf8', env: {} });
const res: { location: string, name: string }[] = JSON.parse(text);
const out = this.#workspaces[rootPath] = res.map(d => ({ sourcePath: d.location, name: d.name }));
await fs.writeFile(cache, JSON.stringify(out), 'utf8');
}
}

@@ -176,0 +183,0 @@ return this.#workspaces[rootPath];

@@ -1,4 +0,1 @@

import { fileURLToPath } from 'url';
import { createRequire } from 'module';
import { path } from './path';

@@ -16,30 +13,6 @@ import { IndexedModule, ManifestIndex } from './manifest-index';

class $RootIndex extends ManifestIndex {
/**
* Load all source modules
*/
static resolveManifestJSON(root: string, file?: string): string {
file = file || path.resolve(root, 'manifest.json');
// IF not a file
if (!file.endsWith('.json')) {
try {
// Try to resolve
const req = createRequire(path.resolve(root, 'node_modules'));
file = req.resolve(`${file}/manifest.json`);
} catch {
// Fallback to assumed node_modules pattern
file = `${root}/node_modules/${file}/manifest.json`;
}
}
return file;
}
#config: Package | undefined;
#srcCache = new Map();
#metadata = new Map<string, FunctionMetadata>();
constructor(output: string = process.env.TRV_OUTPUT ?? process.cwd()) {
super(output, $RootIndex.resolveManifestJSON(output, process.env.TRV_MANIFEST));
}
/**

@@ -50,3 +23,3 @@ * **WARNING**: This is a destructive operation, and should only be called before loading any code

reinitForModule(module: string): void {
this.init(this.root, $RootIndex.resolveManifestJSON(this.root, module));
this.init(`${this.outputRoot}/node_modules/${module}`);
this.#config = undefined;

@@ -59,3 +32,3 @@ }

isMonoRepoRoot(): boolean {
return !!this.manifest.monoRepo && this.manifest.workspacePath === this.manifest.mainPath;
return !!this.manifest.monoRepo && !this.manifest.mainFolder;
}

@@ -67,4 +40,4 @@

async loadSource(): Promise<void> {
for (const { output } of this.findSrc()) {
await import(output);
for (const { import: imp } of this.findSrc()) {
await import(imp);
}

@@ -94,3 +67,3 @@ }

if (!this.#config) {
const { output: mainFolder } = this.getModule(this.manifest.mainModule)!;
const { outputPath } = this.getModule(this.manifest.mainModule)!;
this.#config = {

@@ -102,3 +75,3 @@ ...{

},
...PackageUtil.readPackage(mainFolder)
...PackageUtil.readPackage(outputPath)
};

@@ -114,16 +87,7 @@ }

/**
* Get source file from output location
* Get source file from import location
* @param outputFile
*/
getSourceFile(file: string): string {
if (!this.#srcCache.has(file)) {
if (file.startsWith('file:')) {
this.#srcCache.set(file, path.toPosix(fileURLToPath(file)));
} else {
this.#srcCache.set(file, path.toPosix(file));
}
}
const outputFile = this.#srcCache.get(file)!;
return this.getEntry(outputFile)?.source ?? outputFile;
getSourceFile(importFile: string): string {
return this.getFromImport(importFile)?.sourceFile ?? importFile;
}

@@ -139,4 +103,4 @@

*/
registerFunction(cls: Function, file: string, hash: number, methods?: Record<string, { hash: number }>, abstract?: boolean, synthetic?: boolean): boolean {
const source = this.getSourceFile(file);
registerFunction(cls: Function, fileOrImport: string, hash: number, methods?: Record<string, { hash: number }>, abstract?: boolean, synthetic?: boolean): boolean {
const source = this.getSourceFile(fileOrImport);
const id = this.getId(source, cls.name);

@@ -165,2 +129,20 @@ Object.defineProperty(cls, 'Ⲑid', { value: id });

}
/**
* Get local folders that represent the user's controlled input
*/
getLocalInputFolders(): string[] {
return this.getLocalModules()
.flatMap(x =>
(!this.manifest.monoRepo || x.sourcePath !== this.manifest.workspacePath) ?
[x.sourcePath] : [...Object.keys(x.files)].filter(y => !y.startsWith('$')).map(y => path.resolve(x.sourcePath, y))
);
}
/**
* Get local output folders
*/
getLocalOutputFolders(): string[] {
return this.getLocalModules().map(x => x.outputPath);
}
}

@@ -171,5 +153,5 @@

try {
index = new $RootIndex();
index = new $RootIndex(process.env.TRV_MANIFEST!);
} catch (err) {
if (process.env.TRV_THROW_ROOT_INDEX_ERR) {
if (/prod/i.test(process.env.NODE_ENV ?? '')) {
throw err;

@@ -176,0 +158,0 @@ }

@@ -6,3 +6,3 @@ export type ManifestModuleFileType = 'typings' | 'ts' | 'js' | 'json' | 'package-json' | 'unknown' | 'fixture' | 'md';

'test/fixtures' | 'support/fixtures' | 'support/resources' |
'$other';
'$other' | '$transformer';

@@ -18,4 +18,4 @@ export type ManifestProfile = 'compile' | 'test' | 'doc' | 'build' | 'std';

version: string;
source: string;
output: string;
sourceFolder: string;
outputFolder: string;
profiles: ManifestProfile[];

@@ -31,5 +31,4 @@ parents: string[];

export type ManifestContext = {
manifestFile: string;
mainModule: string;
mainPath: string;
mainFolder: string;
workspacePath: string;

@@ -39,2 +38,3 @@ outputFolder: string;

monoRepo?: boolean;
moduleType: 'module' | 'commonjs';
};

@@ -47,11 +47,2 @@

export type ManifestDeltaEventType = 'added' | 'changed' | 'removed' | 'missing' | 'dirty';
export type ManifestDeltaModule = ManifestModuleCore & { files: Record<string, ManifestModuleFile> };
export type ManifestDeltaEvent = { file: string, type: ManifestDeltaEventType, module: string };
export type ManifestDelta = Record<string, ManifestDeltaEvent[]>;
export type ManifestState = {
manifest: ManifestRoot;
delta: ManifestDelta;
};
export type Package = {

@@ -124,2 +115,2 @@ name: string;

abstract?: boolean;
};
};
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc