UNPKG

salesforce-alm

Version:

This package contains tools, and APIs, for an improved salesforce.com developer experience.

257 lines (255 loc) 10.2 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Workspace = void 0; const path = require("path"); const core_1 = require("@salesforce/core"); const kit_1 = require("@salesforce/kit"); const Messages = require("../messages"); const sourcePathStatusManager_1 = require("./sourcePathStatusManager"); const messages = Messages(); const Package2ConfigFileNames = ['package2-descriptor.json', 'package2-manifest.json']; // eslint-disable-next-line no-redeclare class Workspace extends core_1.ConfigFile { constructor(options) { super(options); this.pathInfos = new Map(); this.trackedPackages = []; this.org = options.org; this.forceIgnore = options.forceIgnore; this.isStateless = options.isStateless; this.workspacePath = options.org.config.getProjectPath(); } async init() { this.logger = await core_1.Logger.child(this.constructor.name); this.options.filePath = path.join('orgs', this.org.name); this.options.filename = Workspace.getFileName(); await super.init(); this.backupPath = `${this.getPath()}.bak`; if (!this.isStateless) { await this.initializeCached(); const pathInfos = this.getContents(); if (kit_1.isEmpty(pathInfos)) { await this.initializeStateFull(); } } else { this.initializeStateless(); } } async initializeCached() { this.logger.debug('Reading workspace from cache'); let workspacePathChanged; const trackedPackages = []; try { const oldSourcePathInfos = [...this.values()]; let oldWorkspacePath; for (const sourcePathInfoObj of oldSourcePathInfos) { if (!sourcePathInfoObj.package) { const packagePath = core_1.SfdxProject.getInstance().getPackageNameFromPath(sourcePathInfoObj.sourcePath); if (packagePath) { sourcePathInfoObj.package = packagePath; } } if (sourcePathInfoObj.isWorkspace) { oldWorkspacePath = sourcePathInfoObj.sourcePath; } if (sourcePathInfoObj.isArtifactRoot) { trackedPackages.push(sourcePathInfoObj.sourcePath); } } this.trackedPackages = trackedPackages; workspacePathChanged = !!oldWorkspacePath && this.workspacePath !== oldWorkspacePath; for (const sourcePathInfoObj of oldSourcePathInfos) { const sourcePathInfo = await sourcePathStatusManager_1.SourcePathInfo.create(sourcePathInfoObj); if (workspacePathChanged) { const oldPath = sourcePathInfo.sourcePath; sourcePathInfo.sourcePath = path.join(this.workspacePath, path.relative(oldWorkspacePath, sourcePathInfo.sourcePath)); this.unset(oldPath); } this.set(sourcePathInfo.sourcePath, sourcePathInfo); } } catch (e) { // Do nothing if the file can't be read, which will cause the workspace to be initialized } if (workspacePathChanged) { await this.write(); } } async initializeStateFull() { this.logger.debug('Initializing statefull workspace'); const packages = core_1.SfdxProject.getInstance() .getUniquePackageDirectories() .map((p) => stripTrailingSlash(p.fullPath)); this.trackedPackages = packages; await this.walkDirectories(packages); await this.write(); } initializeStateless() { this.logger.debug('Initializing stateless workspace'); this.trackedPackages = core_1.SfdxProject.getInstance() .getUniquePackageDirectories() .map((p) => stripTrailingSlash(p.fullPath)); this.setContents({}); } getContents() { return this['contents']; } entries() { // override entries and cast here to avoid casting every entries() call return super.entries(); } static getFileName() { return 'sourcePathInfos.json'; } async rewriteInfos() { await this.initializeStateFull(); } async walkDirectories(directories) { for (const directory of directories) { const exists = await core_1.fs.fileExists(directory); if (!exists) { const error = new Error(messages.getMessage('InvalidPackageDirectory', directory)); error['name'] = 'InvalidProjectWorkspace'; throw error; } await this.walk(directory); } } /** * Walks the directory using native fs.readdir */ async walk(directory, recur) { if (!recur) { await this.handleArtifact(directory, directory); } const files = await core_1.fs.readdir(directory); for (const filename of files) { const sourcePath = path.join(directory, filename); const sourcePathInfo = await this.handleArtifact(sourcePath, directory); if (sourcePathInfo.isDirectory) { await this.walk(sourcePath, true); } } } async handleArtifact(sourcePath, parentDirectory) { const isWorkspace = false; const isArtifactRoot = parentDirectory ? sourcePath === parentDirectory : false; const sourcePathInfo = await sourcePathStatusManager_1.SourcePathInfo.create({ sourcePath, deferContentHash: false, isWorkspace, isArtifactRoot, }); if (this.isValidSourcePath(sourcePathInfo)) { this.set(sourcePath, sourcePathInfo); } return sourcePathInfo; } /** * Check if the given sourcePath should be ignored */ isValidSourcePath(sourcePathInfo) { const sourcePath = sourcePathInfo.sourcePath; let isValid = this.forceIgnore.accepts(sourcePath); const basename = path.basename(sourcePath); const isPackage2ConfigFile = Package2ConfigFileNames.includes(basename); isValid = !basename.startsWith('.') && !basename.endsWith('.dup') && isValid && !isPackage2ConfigFile; if (isValid && !!sourcePathStatusManager_1.SourcePathStatusManager.metadataRegistry) { if (!sourcePathInfo.isDirectory) { if (!sourcePathStatusManager_1.SourcePathStatusManager.metadataRegistry.isValidSourceFilePath(sourcePath)) { const error = new Error(`Unexpected file found in package directory: ${sourcePath}`); error['name'] = 'UnexpectedFileFound'; throw error; } } } // Skip directories/files beginning with '.', end with .dup, and that should be ignored return isValid; } async write() { if (!this.has(this.workspacePath)) { const workspaceSourcePathInfo = await sourcePathStatusManager_1.SourcePathInfo.create({ sourcePath: this.workspacePath, deferContentHash: false, isWorkspace: true, isArtifactRoot: false, }); this.set(this.workspacePath, workspaceSourcePathInfo); } return super.write(); } get(key) { return this.getContents()[key]; } async getInitializedValue(key) { const value = this.get(key); return sourcePathStatusManager_1.SourcePathInfo.create(value); } has(key) { return !!this.get(key); } values() { return super.values(); } async getInitializedValues() { const values = this.values(); const initialized = []; for (const value of values) { initialized.push(await sourcePathStatusManager_1.SourcePathInfo.create(value)); } return initialized; } // @ts-ignore because typescript expects value to be a SourcePathInfo.Json but we want to do the // conversion from a SourcePathInfo instance to SourcePathInfo.Json here instead of relying on whoever // calls this method to do it first. set(key, value) { return super.set(key, value.toJson()); } setMethod(contents, key, value) { contents[key] = value; } async revert() { if (await core_1.fs.fileExists(this.backupPath)) { const backedupContents = await core_1.fs.readFile(this.backupPath, 'UTF-8'); this.setContentsFromObject(JSON.parse(backedupContents)); await this.write(); await core_1.fs.unlink(this.backupPath); } } async backup() { // eslint-disable-next-line @typescript-eslint/no-misused-promises if (this.exists()) { await core_1.fs.writeFile(this.backupPath, JSON.stringify(this.getContents())); } } async read() { try { return await super.read(); } catch (err) { if (err.name === 'JsonDataFormatError') { // This error means that the old sourcePathInfos format is still // in use and so we need to convert it to the new format. const contents = await core_1.fs.readFile(this.getPath(), 'utf-8'); const map = new Map(JSON.parse(contents)); const obj = {}; map.forEach((value, key) => (obj[key] = value)); this.setContentsFromObject(obj); await this.write(); return this.getContents(); } } } } exports.Workspace = Workspace; function stripTrailingSlash(str) { return str.endsWith(path.sep) ? str.slice(0, -1) : str; } //# sourceMappingURL=workspace.js.map