Skip to content

Commit e6b16ce

Browse files
authored
Merge pull request #108 from IBM/feature/abstract_fs
Abstract file systems
2 parents 45f9eea + 7c96d70 commit e6b16ce

29 files changed

+373
-429
lines changed

cli/src/builders/make/folderSettings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as path from "path";
2-
import { getFiles } from "../../utils";
32
import { readFileSync } from "fs";
43
import { warningOut } from "../../cli";
4+
import { ReadFileSystem } from "../../readFileSystem";
5+
import { getFiles } from "../../utils";
56

67
export interface FolderOptions {
78
version?: "0.0.1",

cli/src/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import { BobProject } from "./builders/bob";
1010
import { ImpactMarkdown } from "./builders/imd";
1111
import { allExtensions, referencesFileName } from "./extensions";
1212
import { getBranchLibraryName, getDefaultCompiles } from "./builders/environment";
13-
import { getFiles, renameFiles, replaceIncludes } from './utils';
13+
import { renameFiles, replaceIncludes } from './utils';
1414
import { iProject } from './builders/iProject';
15+
import { ReadFileSystem } from './readFileSystem';
1516

1617
const isCli = process.argv.length >= 2 && (process.argv[1].endsWith(`so`) || process.argv[1].endsWith(`index.js`));
1718

@@ -153,7 +154,8 @@ async function main() {
153154
process.exit(0);
154155
}
155156

156-
const targets = new Targets(cwd);
157+
const fs = new ReadFileSystem();
158+
const targets = new Targets(cwd, fs);
157159

158160
targets.setSuggestions({
159161
includes: cliSettings.fixIncludes,
@@ -165,14 +167,14 @@ async function main() {
165167
let files: string[];
166168

167169
try {
168-
files = getFiles(cwd, scanGlob);
170+
files = await fs.getFiles(cwd, scanGlob);
169171
} catch (e) {
170172
error(e.message || e);
171173
process.exit(1);
172174
}
173175

174176
const referenceFile = path.join(cwd, referencesFileName);
175-
if (existsSync(referenceFile)) {
177+
if (await fs.exists(referenceFile)) {
176178
infoOut(`Found reference file: ${referenceFile}`);
177179
targets.handleRefsFile(referenceFile);
178180
}
@@ -206,7 +208,7 @@ async function main() {
206208

207209
if (cliSettings.lookupFiles && cliSettings.buildFile === `none`) {
208210
for (const value of cliSettings.lookupFiles) {
209-
listDeps(cwd, targets, value);
211+
await listDeps(cwd, targets, value);
210212
}
211213
}
212214

@@ -277,7 +279,7 @@ function initProject(cwd) {
277279
/**
278280
* @param query Can be object (ABCD.PGM) or relative path
279281
*/
280-
function listDeps(cwd: string, targets: Targets, query: string) {
282+
async function listDeps(cwd: string, targets: Targets, query: string) {
281283
const fullPath = path.join(cwd, query);
282284

283285
let [name, type] = query.split(`.`);
@@ -288,7 +290,7 @@ function listDeps(cwd: string, targets: Targets, query: string) {
288290
let theObject = targets.getResolvedObjects().find(o => o.systemName === name && o.type === type);
289291

290292
if (!theObject) {
291-
theObject = targets.resolvePathToObject(fullPath);
293+
theObject = await targets.resolvePathToObject(fullPath);
292294
}
293295

294296
const allDeps = targets.getTargets();

cli/src/readFileSystem.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import fs from 'fs/promises';
2+
import ffs from 'fs';
3+
import glob from "glob";
4+
import path from 'path';
5+
import os from 'os';
6+
import { getFiles } from './utils';
7+
import { scanGlob } from './extensions';
8+
9+
export class ReadFileSystem {
10+
constructor() {}
11+
12+
async getFiles(cwd: string, globPath = scanGlob): Promise<string[]> {
13+
return getFiles(cwd, globPath);
14+
}
15+
16+
readFile(filePath: string): Promise<string> {
17+
return fs.readFile(filePath, { encoding: `utf8` });
18+
}
19+
20+
async exists(filePath: string): Promise<boolean> {
21+
return ffs.existsSync(filePath);
22+
}
23+
}

cli/src/targets.ts

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import glob from 'glob';
22
import path from 'path';
3-
import fs from 'fs/promises';
4-
import fss from 'fs';
53
import Cache from "vscode-rpgle/language/models/cache";
64
import { IncludeStatement } from "vscode-rpgle/language/parserTypes";
75
import { infoOut, warningOut } from './cli';
@@ -16,6 +14,7 @@ import { Logger } from './logger';
1614
import { asPosix, getReferenceObjectsFrom, getSystemNameFromPath, toLocalPath } from './utils';
1715
import { extCanBeProgram, getObjectType } from './builders/environment';
1816
import { isSqlFunction } from './languages/sql';
17+
import { ReadFileSystem } from './readFileSystem';
1918

2019
export type ObjectType = "PGM" | "SRVPGM" | "MODULE" | "FILE" | "BNDDIR" | "DTAARA" | "CMD" | "MENU" | "DTAQ";
2120

@@ -109,7 +108,7 @@ export class Targets {
109108

110109
public logger: Logger;
111110

112-
constructor(private cwd: string) {
111+
constructor(private cwd: string, private fs: ReadFileSystem) {
113112
this.rpgParser = setupParser(this);
114113
this.logger = new Logger();
115114
}
@@ -138,7 +137,17 @@ export class Targets {
138137
this.resolvedObjects[localPath] = ileObject;
139138
}
140139

141-
public resolvePathToObject(localPath: string, newText?: string) {
140+
public async loadProject(withRef?: string) {
141+
if (withRef) {
142+
await this.handleRefsFile(path.join(this.cwd, withRef));
143+
}
144+
145+
const initialFiles = await this.fs.getFiles(this.cwd);
146+
await this.loadObjectsFromPaths(initialFiles);
147+
await Promise.allSettled(initialFiles.map(f => this.parseFile(f)));
148+
}
149+
150+
public async resolvePathToObject(localPath: string, newText?: string) {
142151
if (this.resolvedObjects[localPath]) {
143152
if (newText) this.resolvedObjects[localPath].text = newText;
144153
return this.resolvedObjects[localPath];
@@ -163,7 +172,7 @@ export class Targets {
163172

164173
// If this file is an SQL file, we need to look to see if it has a long name as we need to resolve all names here
165174
if (sqlExtensions.includes(extension.toLowerCase())) {
166-
const ref = this.sqlObjectDataFromPath(localPath);
175+
const ref = await this.sqlObjectDataFromPath(localPath);
167176
if (ref) {
168177
if (ref.object.system) theObject.systemName = ref.object.system.toUpperCase();
169178
if (ref.object.name) theObject.longName = ref.object.name;
@@ -190,17 +199,17 @@ export class Targets {
190199
* @param filePath Fully qualified path to the file. Assumed to exist.
191200
*/
192201
public async handleRefsFile(filePath: string) {
193-
const content = await fs.readFile(filePath, { encoding: `utf-8` });
202+
const content = await this.fs.readFile(filePath);
194203

195204
const pseudoObjects = getReferenceObjectsFrom(content);
196205

197-
pseudoObjects.forEach(ileObject => {
206+
for (const ileObject of pseudoObjects) {
198207
if (!this.searchForObject(ileObject)) {
199208
const key = `${ileObject.systemName}.${ileObject.type}`;
200209
ileObject.reference = true;
201210
this.resolvedObjects[key] = ileObject;
202211
}
203-
});
212+
};
204213
}
205214

206215
public isReferenceObject(ileObject: ILEObject, remove?: boolean) {
@@ -330,7 +339,7 @@ export class Targets {
330339

331340
public loadObjectsFromPaths(paths: string[]) {
332341
// optimiseFileList(paths); //Ensure we load SQL files first
333-
paths.forEach(p => this.resolvePathToObject(p));
342+
return Promise.all(paths.map(p => this.resolvePathToObject(p)));
334343
}
335344

336345
public async parseFile(filePath: string) {
@@ -348,7 +357,7 @@ export class Targets {
348357
const ext = pathDetail.ext.substring(1).toLowerCase();
349358

350359
try {
351-
const content = await fs.readFile(filePath, { encoding: `utf-8` });
360+
const content = await this.fs.readFile(filePath);
352361
const eol = content.indexOf(`\r\n`) >= 0 ? `\r\n` : `\n`;
353362

354363
// Really only applied to rpg
@@ -380,7 +389,8 @@ export class Targets {
380389
);
381390

382391
if (rpgDocs) {
383-
this.createRpgTarget(filePath, rpgDocs, options);
392+
const ileObject = await this.resolvePathToObject(filePath, options.text);
393+
this.createRpgTarget(ileObject, filePath, rpgDocs, options);
384394
}
385395

386396
}
@@ -391,13 +401,15 @@ export class Targets {
391401
const module = new Module();
392402
module.parseStatements(tokens);
393403

394-
this.createClTarget(filePath, module, options);
404+
const ileObject = await this.resolvePathToObject(filePath);
405+
this.createClTarget(ileObject, filePath, module, options);
395406
}
396407
else if (ddsExtension.includes(ext)) {
397408
const ddsFile = new dds();
398409
ddsFile.parse(content.split(eol));
399410

400-
this.createDdsFileTarget(filePath, ddsFile, options);
411+
const ileObject = await this.resolvePathToObject(filePath, options.text);
412+
this.createDdsFileTarget(ileObject, filePath, ddsFile, options);
401413
}
402414
else if (sqlExtensions.includes(ext)) {
403415
const sqlDoc = new Document(content);
@@ -410,7 +422,8 @@ export class Targets {
410422
const module = new Module();
411423
module.parseStatements(tokens);
412424

413-
this.createSrvPgmTarget(filePath, module, options);
425+
const ileObject = await this.resolvePathToObject(filePath, options.text);
426+
this.createSrvPgmTarget(ileObject, filePath, module, options);
414427
}
415428
else if (cmdExtensions.includes(ext)) {
416429
this.createCmdTarget(filePath, options);
@@ -442,8 +455,7 @@ export class Targets {
442455
// Since cmd source doesn't explicity contains deps, we resolve later on
443456
}
444457

445-
private createSrvPgmTarget(localPath: string, module: Module, options: FileOptions = {}) {
446-
const ileObject = this.resolvePathToObject(localPath, options.text);
458+
private createSrvPgmTarget(ileObject: ILEObject, localPath: string, module: Module, options: FileOptions = {}) {
447459
const target: ILEObjectTarget = {
448460
...ileObject,
449461
deps: [],
@@ -515,8 +527,7 @@ export class Targets {
515527
/**
516528
* Handles all DDS types: pf, lf, dspf
517529
*/
518-
private createDdsFileTarget(localPath: string, dds: dds, options: FileOptions = {}) {
519-
const ileObject = this.resolvePathToObject(localPath, options.text);
530+
private createDdsFileTarget(ileObject: ILEObject, localPath: string, dds: dds, options: FileOptions = {}) {
520531
const target: ILEObjectTarget = {
521532
...ileObject,
522533
deps: []
@@ -610,10 +621,8 @@ export class Targets {
610621
this.addNewTarget(target);
611622
}
612623

613-
private createClTarget(localPath: string, module: Module, options: FileOptions = {}) {
624+
private createClTarget(ileObject: ILEObject, localPath: string, module: Module, options: FileOptions = {}) {
614625
const pathDetail = path.parse(localPath);
615-
const sourceName = pathDetail.base;
616-
const ileObject = this.resolvePathToObject(localPath);
617626
const target: ILEObjectTarget = {
618627
...ileObject,
619628
deps: []
@@ -966,10 +975,8 @@ export class Targets {
966975
}
967976
}
968977

969-
private createRpgTarget(localPath: string, cache: Cache, options: FileOptions = {}) {
978+
private createRpgTarget(ileObject: ILEObject, localPath: string, cache: Cache, options: FileOptions = {}) {
970979
const pathDetail = path.parse(localPath);
971-
const ileObject = this.resolvePathToObject(localPath, options.text);
972-
973980
// define internal imports
974981
ileObject.imports = cache.procedures
975982
.filter((proc: any) => proc.keyword[`EXTPROC`])
@@ -1380,9 +1387,9 @@ export class Targets {
13801387
this.createOrAppend(bindingDirectoryTarget, target);
13811388

13821389
// Make sure we can resolve to this service program
1383-
target.exports.forEach(e => {
1390+
for (const e of target.exports) {
13841391
this.resolvedExports[e.toUpperCase()] = target;
1385-
});
1392+
}
13861393
} else {
13871394
// This service program target doesn't have any deps... so, it's not used?
13881395
this.removeObject(target);
@@ -1408,7 +1415,7 @@ export class Targets {
14081415
// Remove any service program deps so we can resolve them cleanly
14091416
currentTarget.deps = currentTarget.deps.filter(d => ![`SRVPGM`].includes(d.type));
14101417

1411-
currentTarget.imports.forEach(importName => {
1418+
for (const importName of currentTarget.imports) {
14121419
// Find if this import resolves to another object
14131420
const possibleSrvPgmDep = this.resolvedExports[importName.toUpperCase()];
14141421
// We can't add a module as a dependency at this step.
@@ -1430,7 +1437,7 @@ export class Targets {
14301437
}
14311438
}
14321439
}
1433-
});
1440+
};
14341441

14351442
// If the program or module has imports that we ca resolve, then we add them as deps
14361443
if (newImports.length > 0) {
@@ -1619,11 +1626,11 @@ export class Targets {
16191626
* Sadly the long name is not typically part of the path name, so we need to
16201627
* find the name inside of the source code.
16211628
*/
1622-
sqlObjectDataFromPath(fullPath: string): ObjectRef | undefined {
1629+
async sqlObjectDataFromPath(fullPath: string): Promise<ObjectRef> {
16231630
const relativePath = this.getRelative(fullPath);
16241631

1625-
if (fss.existsSync(fullPath)) {
1626-
const content = fss.readFileSync(fullPath, { encoding: `utf-8` });
1632+
if (await this.fs.exists(fullPath)) {
1633+
const content = await this.fs.readFile(fullPath);
16271634
const document = new Document(content);
16281635

16291636
const groups = document.getStatementGroups();

cli/src/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import * as path from "path";
77
import * as os from "os"
88
import { ILEObject, ObjectType } from "./targets";
99
import { CommandParameters } from "./builders/environment";
10+
import { ReadFileSystem } from "./readFileSystem";
1011

1112
export function getSystemNameFromPath(inputName: string) {
12-
1313
let baseName = inputName.includes(`-`) ? inputName.split(`-`)[0] : inputName;
1414

1515
// If the name is of valid length, return it
@@ -45,6 +45,9 @@ export function getSystemNameFromPath(inputName: string) {
4545
return systemName.toUpperCase();
4646
}
4747

48+
/**
49+
* @deprecated Use {@link ReadFileSystem.getFiles} instead
50+
*/
4851
export function getFiles(cwd: string, globPath: string): string[] {
4952
let paths: string[] = glob.sync(globPath, {
5053
cwd,

0 commit comments

Comments
 (0)