Comparing version 3.0.0-alpha.3 to 3.0.0-alpha.4
@@ -5,6 +5,16 @@ # Changelog | ||
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-fs/compare/v3.0.0-alpha.3...HEAD). | ||
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-fs/compare/v3.0.0-alpha.4...HEAD). | ||
<a name="v3.0.0-alpha.4"></a> | ||
## [v3.0.0-alpha.4] - 2022-06-13 | ||
### Breaking Changes | ||
- **AbstractAdapter:** Virtual base path must end with slash [`6d1f411`](https://github.com/SAP/ui5-fs/commit/6d1f4117a2b8bb1226540fafeec8341e4966177d) | ||
- **resourceFactory:** Remove #createCollectionsForTree [`a4d15f6`](https://github.com/SAP/ui5-fs/commit/a4d15f61ae0416051658280bfd2f8635c7ddf44e) | ||
### Features | ||
- Add Link-reader and WriterCollection [`a0e5cf3`](https://github.com/SAP/ui5-fs/commit/a0e5cf3ef86a4b0cdc817d08963ed6574740f1bc) | ||
<a name="v3.0.0-alpha.3"></a> | ||
## [v3.0.0-alpha.3] - 2022-04-21 | ||
## [v3.0.0-alpha.3] - 2022-04-26 | ||
### Bug Fixes | ||
@@ -158,2 +168,3 @@ - **FileSystem Adapter:** Use native fs.copy / Skip writing when resource is unchanged ([#370](https://github.com/SAP/ui5-fs/issues/370)) [`9ac6a39`](https://github.com/SAP/ui5-fs/commit/9ac6a39f3cb72e02c2a1298b07c4676a0ee92377) | ||
[v3.0.0-alpha.4]: https://github.com/SAP/ui5-fs/compare/v3.0.0-alpha.3...v3.0.0-alpha.4 | ||
[v3.0.0-alpha.3]: https://github.com/SAP/ui5-fs/compare/v3.0.0-alpha.2...v3.0.0-alpha.3 | ||
@@ -160,0 +171,0 @@ [v3.0.0-alpha.2]: https://github.com/SAP/ui5-fs/compare/v3.0.0-alpha.1...v3.0.0-alpha.2 |
@@ -105,2 +105,16 @@ const randomInt = require("random-int"); | ||
} | ||
/** | ||
* Create a [Link-Reader]{@link module:@ui5/fs.readers.Link} from the current reader | ||
* | ||
* @public | ||
* @param {module:@ui5/fs.reader.Link.PathMapping} pathMapping Link configuration | ||
* @returns {module:@ui5/fs.reader.Link} Link instance | ||
*/ | ||
link(pathMapping) { | ||
const Link = require("./readers/Link"); | ||
return new Link({ | ||
reader: this, | ||
pathMapping | ||
}); | ||
} | ||
@@ -107,0 +121,0 @@ /** |
@@ -30,2 +30,5 @@ const log = require("@ui5/logger").getLogger("resources:adapters:AbstractAdapter"); | ||
super(); | ||
if (!virBasePath.endsWith("/")) { | ||
throw new Error(`Virtual base path must end with a slash: ${virBasePath}`); | ||
} | ||
this._virBasePath = virBasePath; | ||
@@ -71,4 +74,3 @@ this._virBaseDir = virBasePath.slice(0, -1); | ||
return Promise.resolve([ | ||
new Resource({ | ||
project: this.project, | ||
this._createResource({ | ||
statInfo: { // TODO: make closer to fs stat info | ||
@@ -79,2 +81,5 @@ isDirectory: function() { | ||
}, | ||
source: { | ||
adapter: "Abstract" | ||
}, | ||
path: subPath | ||
@@ -121,3 +126,3 @@ }) | ||
if (that._project) { // project is optional | ||
log.verbose(`Project: ${that._project.metadata.name}`); | ||
log.verbose(`Project: ${that._project.getName()}`); | ||
} | ||
@@ -177,4 +182,30 @@ log.verbose(`Virtual base path: ${that._virBaseDir}`); | ||
} | ||
_createResource(parameters) { | ||
if (this._project) { | ||
parameters.project = this._project; | ||
} | ||
return new Resource(parameters); | ||
} | ||
_write(resource) { | ||
if (!this._project) { | ||
return; | ||
} | ||
// Assign project to resource if necessary | ||
if (resource.hasProject()) { | ||
if (resource.getProject() !== this._project) { | ||
throw new Error( | ||
`Unable to write resource associated with project ` + | ||
`${resource.getProject().getName()} into adapter of project ${this._project.getName()}: ` + | ||
resource.getPath()); | ||
} | ||
return; | ||
} | ||
log.verbose(`Associating resource ${resource.getPath()} with project ${this._project.getName()}`); | ||
resource.setProject(this._project); | ||
} | ||
} | ||
module.exports = AbstractAdapter; |
@@ -10,3 +10,2 @@ const log = require("@ui5/logger").getLogger("resources:adapters:FileSystem"); | ||
const {PassThrough} = require("stream"); | ||
const Resource = require("../Resource"); | ||
const AbstractAdapter = require("./AbstractAdapter"); | ||
@@ -64,3 +63,3 @@ | ||
} else { | ||
resolve(new Resource({ | ||
resolve(this._createResource({ | ||
project: this._project, | ||
@@ -101,3 +100,3 @@ statInfo: stat, | ||
} else { | ||
resolve(new Resource({ | ||
resolve(this._createResource({ | ||
project: this._project, | ||
@@ -143,3 +142,3 @@ statInfo: stat, | ||
if (!options.nodir && this._virBasePath.startsWith(virPath)) { | ||
resolve(new Resource({ | ||
resolve(this._createResource({ | ||
project: this._project, | ||
@@ -190,3 +189,3 @@ statInfo: { // TODO: make closer to fs stat info | ||
resolve(new Resource(options)); | ||
resolve(this._createResource(options)); | ||
} | ||
@@ -216,2 +215,3 @@ }); | ||
async _write(resource, {drain, readOnly}) { | ||
super._write(resource); | ||
if (drain && readOnly) { | ||
@@ -218,0 +218,0 @@ throw new Error(`Error while writing resource ${resource.getPath()}: ` + |
const log = require("@ui5/logger").getLogger("resources:adapters:Memory"); | ||
const micromatch = require("micromatch"); | ||
const Resource = require("../Resource"); | ||
const AbstractAdapter = require("./AbstractAdapter"); | ||
@@ -42,4 +41,4 @@ | ||
return [ | ||
new Resource({ | ||
project: this.project, | ||
this._createResource({ | ||
project: this._project, | ||
statInfo: { // TODO: make closer to fs stat info | ||
@@ -50,2 +49,5 @@ isDirectory: function() { | ||
}, | ||
source: { | ||
adapter: "Memory" | ||
}, | ||
path: this._virBasePath.slice(0, -1) | ||
@@ -118,2 +120,3 @@ }) | ||
_write(resource) { | ||
super._write(resource); | ||
return new Promise((resolve, reject) => { | ||
@@ -139,4 +142,7 @@ const relPath = resource.getPath().substr(this._virBasePath.length); | ||
if (!this._virDirs[segment]) { | ||
this._virDirs[segment] = new Resource({ | ||
project: this.project, | ||
this._virDirs[segment] = this._createResource({ | ||
project: this._project, | ||
source: { | ||
adapter: "Memory" | ||
}, | ||
statInfo: { // TODO: make closer to fs stat info | ||
@@ -143,0 +149,0 @@ isDirectory: function() { |
@@ -41,6 +41,6 @@ const stream = require("stream"); | ||
* In some cases this is the most memory-efficient way to supply resource content | ||
* @param {module:@ui5/project.specifications.Project} [parameters.project] Project this resource is associated with | ||
* @param {object} [parameters.source] Experimental, internal parameter. Do not use | ||
* @param {object} [parameters.project] Experimental, internal parameter. Do not use | ||
*/ | ||
constructor({path, statInfo, buffer, string, createStream, stream, source, project}) { | ||
constructor({path, statInfo, buffer, string, createStream, stream, project, source}) { | ||
if (!path) { | ||
@@ -57,3 +57,3 @@ throw new Error("Cannot create Resource: path parameter missing"); | ||
this._name = this._getNameFromPath(path); | ||
this._project = project; // Experimental, internal parameter | ||
this._source = source; // Experimental, internal parameter | ||
@@ -64,2 +64,4 @@ if (this._source) { | ||
} | ||
this.__project = project; // Two underscores since "_project" was widely used in UI5 Tooling 2.0 | ||
this._statInfo = statInfo || { // TODO | ||
@@ -298,29 +300,60 @@ isFile: fnTrue, | ||
*/ | ||
clone() { | ||
async clone() { | ||
const options = await this._getCloneOptions(); | ||
return new Resource(options); | ||
} | ||
async _getCloneOptions() { | ||
const options = { | ||
path: this._path, | ||
statInfo: clone(this._statInfo) | ||
statInfo: clone(this._statInfo), | ||
source: this._source | ||
}; | ||
const addContentOption = () => { | ||
if (this._stream) { | ||
return this._getBufferFromStream().then(function(buffer) { | ||
options.buffer = buffer; | ||
}); | ||
} else { | ||
if (this._createStream) { | ||
options.createStream = this._createStream; | ||
} else if (this._buffer) { | ||
options.buffer = this._buffer; | ||
} | ||
return Promise.resolve(); | ||
} | ||
}; | ||
if (this._stream) { | ||
options.buffer = await this._getBufferFromStream(); | ||
} else if (this._createStream) { | ||
options.createStream = this._createStream; | ||
} else if (this._buffer) { | ||
options.buffer = this._buffer; | ||
} | ||
return addContentOption().then(() => { | ||
return new Resource(options); | ||
}); | ||
return options; | ||
} | ||
/** | ||
* Retrieve the project assigned to the resource | ||
* | ||
* @public | ||
* @returns {module:@ui5/project.specifications.Project} Project this resource is associated with | ||
*/ | ||
getProject() { | ||
return this.__project; | ||
} | ||
/** | ||
* Assign a project to the resource | ||
* | ||
* @public | ||
* @param {module:@ui5/project.specifications.Project} project Project this resource is associated with | ||
*/ | ||
setProject(project) { | ||
if (this.__project) { | ||
throw new Error(`Unable to assign project ${project.getName()} to resource ${this._path}: ` + | ||
`Resource is already associated to project ${this.__project}`); | ||
} | ||
this.__project = project; | ||
} | ||
/** | ||
* Check whether a project has been assigned to the resource | ||
* | ||
* @public | ||
* @returns {boolean} True if the resource is associated with a project | ||
*/ | ||
hasProject() { | ||
return !!this.__project; | ||
} | ||
/** | ||
* Tracing: Get tree for printing out trace | ||
@@ -327,0 +360,0 @@ * |
@@ -1,11 +0,1 @@ | ||
const log = require("@ui5/logger").getLogger("resources:resourceFactory"); | ||
const path = require("path"); | ||
const FsAdapter = require("./adapters/FileSystem"); | ||
const MemAdapter = require("./adapters/Memory"); | ||
const ReaderCollection = require("./ReaderCollection"); | ||
const ReaderCollectionPrioritized = require("./ReaderCollectionPrioritized"); | ||
const DuplexCollection = require("./DuplexCollection"); | ||
const Resource = require("./Resource"); | ||
const hasOwnProperty = Object.prototype.hasOwnProperty; | ||
/** | ||
@@ -20,203 +10,2 @@ * Resource Factory | ||
/** | ||
* Callback to retrieve excludes for a given project | ||
* | ||
* @public | ||
* @callback module:@ui5/fs.resourceFactory~getProjectExcludes | ||
* @param {object} Project | ||
* @returns {string[]} List of glob patterns to exclude | ||
*/ | ||
/** | ||
* Callback to retrieve a prefix to use for a given virtual base path of a project | ||
* | ||
* @public | ||
* @callback module:@ui5/fs.resourceFactory~getVirtualBasePathPrefix | ||
* @param {object} parameters Parameters | ||
* @param {object} parameters.project Project | ||
* @param {object} parameters.virBasePath virtual base path to prefix | ||
* @returns {string} Prefix for the virtual base path | ||
*/ | ||
/** | ||
* Creates resource reader collections for a (sub-)tree. Returns an object of resource readers: | ||
* <pre> | ||
* { | ||
* source: Resource reader for source resources | ||
* dependencies: Resource readers for dependency resources | ||
* } | ||
* </pre> | ||
* | ||
* @public | ||
* @param {object} tree A (sub-)tree | ||
* @param {object} [parameters] Parameters | ||
* @param {module:@ui5/fs.resourceFactory~getProjectExcludes} [parameters.getProjectExcludes] | ||
* Callback to retrieve the exclude globs of a project | ||
* @param {module:@ui5/fs.resourceFactory~getVirtualBasePathPrefix} [parameters.getVirtualBasePathPrefix] | ||
* Callback to retrieve a prefix for a given virtual base path of a project if required | ||
* @param {object} [parameters.virtualReaders] Experimental, internal parameter. Do not use | ||
* @returns {object} Object containing <code>source</code> and <code>dependencies</code> resource readers | ||
*/ | ||
createCollectionsForTree(tree, { | ||
getProjectExcludes, getVirtualBasePathPrefix, virtualReaders={} | ||
} = {}) { | ||
// TODO 3.0: virtualReaders is private API. The virtual reader of a project should be stored on the | ||
// project itself. This requires projects to become objects independent from the dependency tree. | ||
// Also see: https://github.com/SAP/ui5-project/issues/122 | ||
const dependencyCollection = []; | ||
const dependencyPathIndex = {}; | ||
const virtualReaderIndex = {}; | ||
const sourceResourceLocators = []; | ||
function processDependencies(project) { | ||
if (project.resources && project.resources.pathMappings) { | ||
const fsAdapters = []; | ||
for (const virBasePath in project.resources.pathMappings) { | ||
if (hasOwnProperty.call(project.resources.pathMappings, virBasePath)) { | ||
// Prevent duplicate dependency resource locators | ||
const fsPath = project.resources.pathMappings[virBasePath]; | ||
const fsBasePath = path.join(project.path, fsPath); | ||
const key = virBasePath + fsBasePath; | ||
if (dependencyPathIndex[key]) { | ||
continue; | ||
} | ||
dependencyPathIndex[key] = true; | ||
// Create an fs adapter for every path mapping | ||
const fsAdapter = resourceFactory._createFsAdapterForVirtualBasePath({ | ||
project, | ||
virBasePath, | ||
getProjectExcludes, | ||
getVirtualBasePathPrefix | ||
}); | ||
fsAdapters.push(fsAdapter); | ||
} | ||
} | ||
if (!virtualReaderIndex[project.metadata.name] && virtualReaders[project.metadata.name]) { | ||
// Mix-in virtual reader of dependency if available and not already added | ||
virtualReaderIndex[project.metadata.name] = true; | ||
const virtualReader = virtualReaders[project.metadata.name]; | ||
const readerCollection = new ReaderCollectionPrioritized({ | ||
name: `fs & vir reader collection for project ${project.metadata.name}`, | ||
readers: [virtualReader, ...fsAdapters] | ||
}); | ||
dependencyCollection.push(readerCollection); | ||
} else { | ||
dependencyCollection.push(...fsAdapters); | ||
} | ||
} | ||
project.dependencies.forEach(function(depProject) { | ||
processDependencies(depProject); | ||
}); | ||
} | ||
if (tree.resources && tree.resources.pathMappings) { | ||
for (const virBasePath in tree.resources.pathMappings) { | ||
if (hasOwnProperty.call(tree.resources.pathMappings, virBasePath)) { | ||
// Create an fs adapter for every path mapping | ||
const fsAdapter = resourceFactory._createFsAdapterForVirtualBasePath({ | ||
project: tree, | ||
virBasePath, | ||
getProjectExcludes, | ||
getVirtualBasePathPrefix | ||
}); | ||
sourceResourceLocators.push(fsAdapter); | ||
} | ||
} | ||
} | ||
tree.dependencies.forEach(function(project) { | ||
processDependencies(project); | ||
}); | ||
const source = new ReaderCollection({ | ||
name: "source of " + tree.metadata.name, | ||
readers: sourceResourceLocators | ||
}); | ||
const dependencies = new ReaderCollection({ | ||
name: "dependencies of " + tree.metadata.name, | ||
readers: dependencyCollection | ||
}); | ||
return { | ||
source, | ||
dependencies | ||
}; | ||
}, | ||
/** | ||
* Creates a FileSystem adapter mapping to the given virtual base path based on the given projects | ||
* configuration. | ||
* | ||
* @param {object} parameters Parameters | ||
* @param {Project} parameters.project A project | ||
* @param {string} parameters.virBasePath Virtual base path to create the adapter for | ||
* @param {module:@ui5/fs.resourceFactory~getProjectExcludes} [parameters.getProjectExcludes] | ||
* Callback to retrieve the exclude glob of a project | ||
* @param {module:@ui5/fs.resourceFactory~getVirtualBasePathPrefix} [parameters.getVirtualBasePathPrefix] | ||
* Callback to retrieve the exclude glob of a project | ||
* @returns {Promise<string[]>} Promise resolving to list of normalized glob patterns | ||
*/ | ||
_createFsAdapterForVirtualBasePath({ | ||
project, virBasePath, getProjectExcludes, getVirtualBasePathPrefix | ||
}) { | ||
const fsPath = project.resources.pathMappings[virBasePath]; | ||
const fsBasePath = path.join(project.path, fsPath); | ||
let pathExcludes; | ||
if (getProjectExcludes) { | ||
pathExcludes = getProjectExcludes(project); | ||
} | ||
if (getVirtualBasePathPrefix) { | ||
const virBasePathPrefix = getVirtualBasePathPrefix({project, virBasePath}); | ||
if (virBasePathPrefix) { | ||
log.verbose(`Prefixing virtual base path ${virBasePath} of project ${project.metadata.name} ` + | ||
`${virBasePathPrefix}...`); | ||
virBasePath = virBasePathPrefix + virBasePath; | ||
log.verbose(`New virtual base path: ${virBasePath}`); | ||
if (pathExcludes) { | ||
const normalizedPatterns = pathExcludes.map((pattern) => { | ||
return resourceFactory._prefixGlobPattern(pattern, virBasePathPrefix); | ||
}); | ||
pathExcludes = Array.prototype.concat.apply([], normalizedPatterns); | ||
} | ||
} | ||
} | ||
return resourceFactory.createAdapter({ | ||
fsBasePath, | ||
virBasePath, | ||
excludes: pathExcludes, | ||
project | ||
}); | ||
}, | ||
/** | ||
* Normalizes virtual glob patterns by prefixing them with | ||
* a given virtual base directory path | ||
* | ||
* @param {string} virPattern glob pattern for virtual directory structure | ||
* @param {string} virBaseDir virtual base directory path to prefix the given patterns with | ||
* @returns {Promise<string[]>} Promise resolving to list of normalized glob patterns | ||
*/ | ||
_prefixGlobPattern(virPattern, virBaseDir) { | ||
const minimatch = require("minimatch"); | ||
const mm = new minimatch.Minimatch(virPattern); | ||
const resultGlobs = []; | ||
for (let i = 0; i < mm.globSet.length; i++) { | ||
let resultPattern = path.posix.join(virBaseDir, mm.globSet[i]); | ||
if (mm.negate) { | ||
resultPattern = "!" + resultPattern; | ||
} | ||
resultGlobs.push(resultPattern); | ||
} | ||
return resultGlobs; | ||
}, | ||
/** | ||
* Creates a resource <code>ReaderWriter</code>. | ||
@@ -237,4 +26,6 @@ * | ||
if (fsBasePath) { | ||
const FsAdapter = require("./adapters/FileSystem"); | ||
return new FsAdapter({fsBasePath, virBasePath, project, excludes}); | ||
} else { | ||
const MemAdapter = require("./adapters/Memory"); | ||
return new MemAdapter({virBasePath, project, excludes}); | ||
@@ -244,3 +35,54 @@ } | ||
/** | ||
* Creates an adapter and wraps it in a ReaderCollection | ||
* | ||
* @public | ||
* @param {object} parameters Parameters | ||
* @param {string} parameters.virBasePath Virtual base path | ||
* @param {string} [parameters.fsBasePath] File system base path | ||
* @param {object} [parameters.project] Experimental, internal parameter. Do not use | ||
* @param {string[]} [parameters.excludes] List of glob patterns to exclude | ||
* @param {string} [parameters.name] Name for the reader collection | ||
* @returns {module:@ui5/fs.ReaderCollection} Reader collection wrapping an adapter | ||
*/ | ||
createReader({fsBasePath, virBasePath, project, excludes = [], name}) { | ||
const normalizedExcludes = excludes.map((pattern) => { | ||
return resourceFactory.prefixGlobPattern(pattern, virBasePath); | ||
}); | ||
const ReaderCollection = require("./ReaderCollection"); | ||
return new ReaderCollection({ | ||
name, | ||
readers: [resourceFactory.createAdapter({ | ||
fsBasePath, | ||
virBasePath, | ||
project, | ||
excludes: normalizedExcludes | ||
})] | ||
}); | ||
}, | ||
createReaderCollection({name, readers}) { | ||
const ReaderCollection = require("./ReaderCollection"); | ||
return new ReaderCollection({ | ||
name, | ||
readers | ||
}); | ||
}, | ||
createReaderCollectionPrioritized({name, readers}) { | ||
const ReaderCollectionPrioritized = require("./ReaderCollectionPrioritized"); | ||
return new ReaderCollectionPrioritized({ | ||
name, | ||
readers | ||
}); | ||
}, | ||
createWriterCollection({name, writerMapping}) { | ||
const WriterCollection = require("./WriterCollection"); | ||
return new WriterCollection({ | ||
name, | ||
writerMapping | ||
}); | ||
}, | ||
/** | ||
@@ -254,2 +96,3 @@ * Creates a <code>Resource</code>. Accepts the same parameters as the Resource constructor. | ||
createResource(parameters) { | ||
const Resource = require("./Resource"); | ||
return new Resource(parameters); | ||
@@ -275,3 +118,6 @@ }, | ||
createWorkspace({reader, writer, virBasePath = "/", name = "vir & fs source"}) { | ||
const DuplexCollection = require("./DuplexCollection"); | ||
if (!writer) { | ||
const MemAdapter = require("./adapters/Memory"); | ||
writer = new MemAdapter({ | ||
@@ -287,2 +133,27 @@ virBasePath | ||
}); | ||
}, | ||
/** | ||
* Normalizes virtual glob patterns by prefixing them with | ||
* a given virtual base directory path | ||
* | ||
* @param {string} virPattern glob pattern for virtual directory structure | ||
* @param {string} virBaseDir virtual base directory path to prefix the given patterns with | ||
* @returns {string[]} A list of normalized glob patterns | ||
*/ | ||
prefixGlobPattern(virPattern, virBaseDir) { | ||
const path = require("path"); | ||
const minimatch = require("minimatch"); | ||
const mm = new minimatch.Minimatch(virPattern); | ||
const resultGlobs = []; | ||
for (let i = 0; i < mm.globSet.length; i++) { | ||
let resultPattern = path.posix.join(virBaseDir, mm.globSet[i]); | ||
if (mm.negate) { | ||
resultPattern = "!" + resultPattern; | ||
} | ||
resultGlobs.push(resultPattern); | ||
} | ||
return Array.prototype.concat.apply([], resultGlobs); | ||
} | ||
@@ -289,0 +160,0 @@ }; |
@@ -1,28 +0,29 @@ | ||
const tagNamespaceRegExp = new RegExp("^[a-z][a-z0-9]*$"); // part before the colon | ||
const tagNameRegExp = new RegExp("^[A-Z][A-Za-z0-9]+$"); // part after the colon | ||
const tagNamespaceRegExp = /^[a-z][a-z0-9]+$/; // part before the colon | ||
const tagNameRegExp = /^[A-Z][A-Za-z0-9]+$/; // part after the colon | ||
const ResourceFacade = require("./ResourceFacade"); | ||
class ResourceTagCollection { | ||
constructor({allowedTags, superCollection}) { | ||
if (!allowedTags || !allowedTags.length) { | ||
throw new Error(`Missing parameter 'allowedTags'`); | ||
} | ||
constructor({allowedTags = [], allowedNamespaces = [], tags}) { | ||
this._allowedTags = allowedTags; // Allowed tags are validated during use | ||
this._allowedNamespaces = allowedNamespaces; | ||
if (superCollection) { | ||
this._superCollection = superCollection; | ||
this._superTags = this._superCollection.getAcceptedTags(); | ||
if (allowedNamespaces.length) { | ||
let allowedNamespacesRegExp = allowedNamespaces.reduce((regex, tagNamespace, idx) => { | ||
// Ensure alphanum namespace to ensure working regex | ||
if (!tagNamespaceRegExp.test(tagNamespace)) { | ||
throw new Error(`Invalid namespace ${tagNamespace}: ` + | ||
`Namespace must be alphanumeric, lowercase and start with a letter`); | ||
} | ||
return `${regex}${idx === 0 ? "" : "|"}${tagNamespace}`; | ||
}, "^(?:"); | ||
allowedNamespacesRegExp += "):.+$"; | ||
this._allowedNamespacesRegExp = new RegExp(allowedNamespacesRegExp); | ||
} else { | ||
this._superTags = []; | ||
this._allowedNamespacesRegExp = null; | ||
} | ||
// No validation of tag names here since we might remove/ignore | ||
// this parameter in the future and generally allow all tags | ||
this._allowedTags = Object.freeze(allowedTags); | ||
this._pathTags = {}; | ||
this._pathTags = tags || {}; | ||
} | ||
setTag(resourcePath, tag, value = true) { | ||
if (this._superTags.includes(tag)) { | ||
return this._superCollection.setTag(resourcePath, tag, value); | ||
} | ||
resourcePath = this._getPath(resourcePath); | ||
@@ -39,6 +40,2 @@ this._validateTag(tag); | ||
clearTag(resourcePath, tag) { | ||
if (this._superTags.includes(tag)) { | ||
return this._superCollection.clearTag(resourcePath, tag); | ||
} | ||
resourcePath = this._getPath(resourcePath); | ||
@@ -53,6 +50,2 @@ this._validateTag(tag); | ||
getTag(resourcePath, tag) { | ||
if (this._superTags.includes(tag)) { | ||
return this._superCollection.getTag(resourcePath, tag); | ||
} | ||
resourcePath = this._getPath(resourcePath); | ||
@@ -66,9 +59,20 @@ this._validateTag(tag); | ||
getAcceptedTags() { | ||
return [...this._allowedTags, ...this._superTags]; | ||
getAllTags() { | ||
return this._pathTags; | ||
} | ||
acceptsTag(tag) { | ||
if (this._allowedTags.includes(tag) || this._allowedNamespacesRegExp?.test(tag)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
_getPath(resourcePath) { | ||
if (typeof resourcePath !== "string") { | ||
resourcePath = resourcePath.getPath(); | ||
if (resourcePath instanceof ResourceFacade) { | ||
resourcePath = resourcePath.getConcealedResource().getPath(); | ||
} else { | ||
resourcePath = resourcePath.getPath(); | ||
} | ||
} | ||
@@ -82,13 +86,8 @@ if (!resourcePath) { | ||
_validateTag(tag) { | ||
if (!this._allowedTags.includes(tag)) { | ||
throw new Error(`Invalid Tag: Not found in list of allowed tags. Allowed tags: ` + | ||
this._allowedTags.join(", ")); | ||
} | ||
if (!tag.includes(":")) { | ||
throw new Error(`Invalid Tag: Colon required after namespace`); | ||
throw new Error(`Invalid Tag "${tag}": Colon required after namespace`); | ||
} | ||
const parts = tag.split(":"); | ||
if (parts.length > 2) { | ||
throw new Error(`Invalid Tag: Expected exactly one colon but found ${parts.length - 1}`); | ||
throw new Error(`Invalid Tag "${tag}": Expected exactly one colon but found ${parts.length - 1}`); | ||
} | ||
@@ -99,7 +98,14 @@ | ||
throw new Error( | ||
`Invalid Tag: Namespace part must be alphanumeric, lowercase and start with a letter`); | ||
`Invalid Tag "${tag}": Namespace part must be alphanumeric, lowercase and start with a letter`); | ||
} | ||
if (!tagNameRegExp.test(tagName)) { | ||
throw new Error(`Invalid Tag: Name part must be alphanumeric and start with a capital letter`); | ||
throw new Error(`Invalid Tag "${tag}": Name part must be alphanumeric and start with a capital letter`); | ||
} | ||
if (!this.acceptsTag(tag)) { | ||
throw new Error( | ||
`Tag "${tag}" not accepted by this collection. Accepted tags are: ` + | ||
`${this._allowedTags.join(", ") || "*none*"}. Accepted namespaces are: ` + | ||
`${this._allowedNamespaces.join(", ") || "*none*"}`); | ||
} | ||
} | ||
@@ -106,0 +112,0 @@ |
@@ -16,2 +16,5 @@ const log = require("@ui5/logger").getLogger("resources:tracing:Trace"); | ||
constructor(name) { | ||
if (!log.isLevelEnabled("silly")) { | ||
return; | ||
} | ||
this._name = name; | ||
@@ -26,2 +29,5 @@ this._startTime = process.hrtime(); | ||
globCall() { | ||
if (!log.isLevelEnabled("silly")) { | ||
return; | ||
} | ||
this._globCalls++; | ||
@@ -32,2 +38,5 @@ summaryTrace.globCall(); | ||
pathCall() { | ||
if (!log.isLevelEnabled("silly")) { | ||
return; | ||
} | ||
this._pathCalls++; | ||
@@ -38,2 +47,5 @@ summaryTrace.pathCall(); | ||
collection(name) { | ||
if (!log.isLevelEnabled("silly")) { | ||
return; | ||
} | ||
const collection = this._collections[name]; | ||
@@ -51,2 +63,5 @@ if (collection) { | ||
printReport() { | ||
if (!log.isLevelEnabled("silly")) { | ||
return; | ||
} | ||
let report = ""; | ||
@@ -75,7 +90,7 @@ const timeDiff = process.hrtime(this._startTime); | ||
if (this._globCalls && this._pathCalls) { | ||
log.verbose(report); | ||
log.silly(report); | ||
} else if (this._globCalls) { | ||
logGlobs.verbose(report); | ||
logGlobs.silly(report); | ||
} else { | ||
logPaths.verbose(report); | ||
logPaths.silly(report); | ||
} | ||
@@ -82,0 +97,0 @@ |
{ | ||
"name": "@ui5/fs", | ||
"version": "3.0.0-alpha.3", | ||
"version": "3.0.0-alpha.4", | ||
"description": "UI5 Tooling - File System Abstraction", | ||
@@ -77,6 +77,6 @@ "author": { | ||
"check-coverage": true, | ||
"statements": 85, | ||
"branches": 75, | ||
"functions": 80, | ||
"lines": 85, | ||
"statements": 76, | ||
"branches": 72, | ||
"functions": 70, | ||
"lines": 76, | ||
"watermarks": { | ||
@@ -110,2 +110,3 @@ "statements": [ | ||
"clone": "^2.1.0", | ||
"escape-string-regexp": "^4.0.0", | ||
"globby": "^11.1.0", | ||
@@ -112,0 +113,0 @@ "graceful-fs": "^4.2.9", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
122713
28
2680
10
+ Addedescape-string-regexp@^4.0.0
+ Addedescape-string-regexp@4.0.0(transitive)