Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cmoesel committed Oct 15, 2024
1 parent 4c1ebef commit 94c1d04
Show file tree
Hide file tree
Showing 12 changed files with 350 additions and 48 deletions.
64 changes: 34 additions & 30 deletions src/cache/DiskBasedPackageCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,43 +90,47 @@ export class DiskBasedPackageCache implements PackageCache {
const invalidFileCounts = new Map<string, number>();
const resourcePaths: string[] = [];
this.localResourceFolders.forEach(folder => {
let spreadSheetCount = 0;
let invalidFileCount = 0;
fs.readdirSync(folder, { withFileTypes: true })
.filter(entry => {
if (!entry.isFile()) {
return false;
} else if (/\.json$/i.test(entry.name)) {
return true;
} else if (/-spreadsheet.xml/i.test(entry.name)) {
spreadSheetCount++;
this.log(
'debug',
`Skipped spreadsheet XML file: ${path.resolve(entry.path, entry.name)}`
);
return false;
} else if (/\.xml/i.test(entry.name)) {
const xml = fs.readFileSync(path.resolve(entry.path, entry.name)).toString();
if (/<\?mso-application progid="Excel\.Sheet"\?>/m.test(xml)) {
try {
let spreadSheetCount = 0;
let invalidFileCount = 0;
fs.readdirSync(folder, { withFileTypes: true })
.filter(entry => {
if (!entry.isFile()) {
return false;
} else if (/\.json$/i.test(entry.name)) {
return true;
} else if (/-spreadsheet.xml/i.test(entry.name)) {
spreadSheetCount++;
this.log(
'debug',
`Skipped spreadsheet XML file: ${path.resolve(entry.path, entry.name)}`
);
return false;
} else if (/\.xml/i.test(entry.name)) {
const xml = fs.readFileSync(path.resolve(entry.path, entry.name)).toString();
if (/<\?mso-application progid="Excel\.Sheet"\?>/m.test(xml)) {
spreadSheetCount++;
this.log(
'debug',
`Skipped spreadsheet XML file: ${path.resolve(entry.path, entry.name)}`
);
return false;
}
return true;
}
return true;
}
invalidFileCount++;
this.log(
'debug',
`Skipped non-JSON / non-XML file: ${path.resolve(entry.path, entry.name)}`
);
return false;
})
.forEach(entry => resourcePaths.push(path.resolve(entry.path, entry.name)));
spreadSheetCounts.set(folder, spreadSheetCount);
invalidFileCounts.set(folder, invalidFileCount);
invalidFileCount++;
this.log(
'debug',
`Skipped non-JSON / non-XML file: ${path.resolve(entry.path, entry.name)}`
);
return false;
})
.forEach(entry => resourcePaths.push(path.resolve(entry.path, entry.name)));
spreadSheetCounts.set(folder, spreadSheetCount);
invalidFileCounts.set(folder, invalidFileCount);
} catch {
this.log('error', `Failed to load resources from local path: ${folder}`);
}
});
spreadSheetCounts.forEach((count, folder) => {
if (count) {
Expand Down
17 changes: 12 additions & 5 deletions src/db/SQLJSPackageDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ const INSERT_RESOURCE = `INSERT INTO resource
:resourcePath
);`;

const SD_FLAVORS = ['Extension', 'Logical', 'Profile', 'Resource', 'Type'];

export class SQLJSPackageDB implements PackageDB {
private insertPackageStmt: Statement;
private insertResourceStmt: Statement;
Expand Down Expand Up @@ -233,16 +235,20 @@ export class SQLJSPackageDB implements PackageDB {
key = '';
}
// TODO: Upgrade to class-level property once query and parameters are sorted out
// TODO: Support versions when canonical form is used
const [keyText, ...keyVersion] = key.split('|');
const bindStmt: { [key: string]: string } = {};
let findStmt = 'SELECT * FROM resource WHERE ';
if (key !== '*') {
if (keyText !== '*') {
// special case for selecting all
bindStmt[':key'] = key;
bindStmt[':key'] = keyText;
findStmt += '(id = :key OR name = :key OR url = :key)';
} else {
findStmt += '1';
}
if (keyVersion.length) {
bindStmt[':version'] = keyVersion.join('|');
findStmt += ' AND version = :version';
}
if (options.scope?.length) {
const [packageName, ...packageVersion] = options.scope.split('|');
bindStmt[':packageName'] = packageName;
Expand All @@ -255,9 +261,10 @@ export class SQLJSPackageDB implements PackageDB {
if (options.type?.length) {
const conditions = options.type.map((t, i) => {
bindStmt[`:type${i}`] = t;
return `(sdFlavor = :type${i} OR resourceType = :type${i})`;
const field = SD_FLAVORS.includes(t) ? 'sdFlavor' : 'resourceType';
return `${field} = :type${i}`;
});
findStmt += ` AND (${conditions.join(' OR ')}) ORDER BY ${conditions.map(c => `${c} DESC`).join(', ')}, rowid DESC`;
findStmt += ` AND (${conditions.join(' OR ')}) ORDER BY ${conditions.map(c => `(${c} OR NULL) DESC`).join(', ')}, rowid DESC`;
} else {
findStmt += ' ORDER BY rowid DESC';
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './db';
export * from './loader';
export * from './package';
export * from './registry';
export * from './virtual';
84 changes: 72 additions & 12 deletions src/loader/BasePackageLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CurrentBuildClient } from '../current';
import { PackageDB } from '../db';
import { InvalidResourceError } from '../errors';
import { FindResourceInfoOptions, PackageInfo, PackageStats, ResourceInfo } from '../package';
import { VirtualPackage } from '../virtual';
import { RegistryClient } from '../registry';
import { LogFunction } from '../utils';
import { PackageCache } from '../cache/PackageCache';
Expand All @@ -14,6 +15,8 @@ export type BasePackageLoaderOptions = {

export class BasePackageLoader implements PackageLoader {
private log: LogFunction;
private virtualPackages: Map<string, VirtualPackage>;

constructor(
private packageDB: PackageDB,
private packageCache: PackageCache,
Expand All @@ -22,6 +25,7 @@ export class BasePackageLoader implements PackageLoader {
options: BasePackageLoaderOptions = {}
) {
this.log = options.log ?? (() => {});
this.virtualPackages = new Map<string, VirtualPackage>();
}

async loadPackage(name: string, version: string): Promise<LoadStatus> {
Expand Down Expand Up @@ -77,14 +81,55 @@ export class BasePackageLoader implements PackageLoader {
return LoadStatus.LOADED;
}

// async loadLocalPackage(
// name: string,
// version: string,
// packagePath: string,
// strict: boolean
// ): Promise<PackageLoadStatus> {
// return PackageLoadStatus.FAILED;
// }
async loadVirtualPackage(pkg: VirtualPackage): Promise<LoadStatus> {
// Ensure package.json has at least the name and version
const packageJSON = pkg.getPackageJSON();
if (
packageJSON?.name == null ||
packageJSON.name.trim() === '' ||
packageJSON.version == null ||
packageJSON.version.trim() == ''
) {
this.log(
'error',
`Failed to load virtual package ${packageJSON?.name ?? '<unknown>'}#${packageJSON?.version ?? '<unknown>'} because the provided packageJSON did not have a valid name and/or version`
);
return LoadStatus.FAILED;
}

// If it's already loaded, then there's nothing to do
if (this.getPackageLoadStatus(packageJSON.name, packageJSON.version) === LoadStatus.LOADED) {
this.log('info', `Virtual package ${packageJSON.name}#${packageJSON.version} already loaded`);
return LoadStatus.LOADED;
}

// Store the virtual package by its name#version key
const packageKey = `${packageJSON.name}#${packageJSON.version}`;
this.virtualPackages.set(packageKey, pkg);

// Save the package info
const info: PackageInfo = {
name: packageJSON.name,
version: packageJSON.version,
packagePath: `virtual:${packageKey}`,
packageJSONPath: `virtual:${packageKey}:package.json`
};
this.packageDB.savePackageInfo(info);

// Register the resources
await pkg.registerResources((key: string, resource: any) => {
this.loadResource(
`virtual:${packageKey}:${key}`,
resource,
packageJSON.name,
packageJSON.version
);
});

const stats = this.packageDB.getPackageStats(packageJSON.name, packageJSON.version);
this.log('info', `Loaded virtual package ${packageKey} with ${stats.resourceCount} resources`);
return LoadStatus.LOADED;
}

private loadPackageFromCache(name: string, version: string) {
// Ensure the package is cached
Expand All @@ -103,6 +148,7 @@ export class BasePackageLoader implements PackageLoader {
};

if (name === 'LOCAL' && version === 'LOCAL') {
// it's a list of ;-separated paths, so don't try to resolve it
info.packagePath = packagePath;
} else {
const packageJSONPath = this.packageCache.getPackageJSONPath(name, version);
Expand Down Expand Up @@ -258,6 +304,20 @@ export class BasePackageLoader implements PackageLoader {
return isStale;
}

private getResourceAtPath(resourcePath: string): any {
if (/^virtual:/.test(resourcePath)) {
const [, packageKey, resourceKey] = resourcePath.match(/^virtual:([^:]+):(.*)$/);
if (packageKey && resourceKey) {
const pkg = this.virtualPackages.get(packageKey);
return resourceKey === 'package.json'
? pkg?.getPackageJSON()
: pkg?.getResourceByKey(resourceKey);
}
} else {
return this.packageCache.getResourceAtPath(resourcePath);
}
}

getPackageLoadStatus(name: string, version: string): LoadStatus {
const pkg = this.packageDB.findPackageInfo(name, version);
if (pkg) {
Expand All @@ -278,14 +338,14 @@ export class BasePackageLoader implements PackageLoader {
return this.findPackageInfos(name)
.filter(info => info.packageJSONPath)
.map(info => {
return this.packageCache.getResourceAtPath(info.packageJSONPath);
return this.getResourceAtPath(info.packageJSONPath);
});
}

findPackageJSON(name: string, version: string) {
const info = this.findPackageInfo(name, version);
if (info?.packageJSONPath) {
return this.packageCache.getResourceAtPath(info.packageJSONPath);
return this.getResourceAtPath(info.packageJSONPath);
}
}

Expand All @@ -301,14 +361,14 @@ export class BasePackageLoader implements PackageLoader {
return this.findResourceInfos(key, options)
.filter(info => info.resourcePath)
.map(info => {
return this.packageCache.getResourceAtPath(info.resourcePath);
return this.getResourceAtPath(info.resourcePath);
});
}

findResourceJSON(key: string, options?: FindResourceInfoOptions) {
const info = this.findResourceInfo(key, options);
if (info?.resourcePath) {
return this.packageCache.getResourceAtPath(info.resourcePath);
return this.getResourceAtPath(info.resourcePath);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/loader/PackageLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FindResourceInfoOptions, PackageInfo, ResourceInfo } from '../package';
import { FindResourceInfoOptions, PackageInfo, ResourceInfo, VirtualPackage } from '../package';

export enum LoadStatus {
LOADED = 'LOADED',
Expand All @@ -8,6 +8,7 @@ export enum LoadStatus {

export interface PackageLoader {
loadPackage(name: string, version: string): Promise<LoadStatus>;
loadVirtualPackage(pkg: VirtualPackage): Promise<LoadStatus>;
getPackageLoadStatus(name: string, version: string): LoadStatus;
findPackageInfos(name: string): PackageInfo[];
findPackageInfo(name: string, version: string): PackageInfo | undefined;
Expand Down
7 changes: 7 additions & 0 deletions src/package/PackageJSON.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// A minimal package JSON signature
export type PackageJSON = {
name: string;
version: string;
dependencies?: { [key: string]: string };
[key: string]: any;
};
2 changes: 2 additions & 0 deletions src/package/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './PackageInfo';
export * from './PackageJSON';
export * from './PackageStats';
export * from './ResourceInfo';
export * from '../virtual/VirtualPackage';
Loading

0 comments on commit 94c1d04

Please sign in to comment.