Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] AbstractAdapter: Add excludes option #140

Merged
merged 17 commits into from
May 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions lib/adapters/AbstractAdapter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const log = require("@ui5/logger").getLogger("resources:adapters:AbstractAdapter");
const minimatch = require("minimatch");
const micromatch = require("micromatch");
const AbstractReaderWriter = require("../AbstractReaderWriter");
const Resource = require("../Resource");

Expand All @@ -18,17 +19,19 @@ class AbstractAdapter extends AbstractReaderWriter {
* @public
* @param {Object} parameters Parameters
* @param {string} parameters.virBasePath Virtual base path
* @param {string[]} [parameters.excludes] List of glob patterns to exclude
*/
constructor({virBasePath, project}) {
constructor({virBasePath, excludes = [], project}) {
if (new.target === AbstractAdapter) {
throw new TypeError("Class 'AbstractAdapter' is abstract");
}
super();
this._virBasePath = virBasePath;
this._virBaseDir = virBasePath.slice(0, -1);
this._excludes = excludes;
this._excludesNegated = excludes.map((pattern) => `!${pattern}`);
this._project = project;
}

/**
* Locates resources by glob.
*
Expand All @@ -42,14 +45,21 @@ class AbstractAdapter extends AbstractReaderWriter {
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources
*/
_byGlob(virPattern, options = {nodir: true}, trace) {
const excludes = this._excludesNegated;

if (!(virPattern instanceof Array)) {
virPattern = [virPattern];
}

// Append static exclude patterns
virPattern = Array.prototype.concat.apply(virPattern, excludes);

return Promise.all(virPattern.map(this._normalizePattern, this)).then((patterns) => {
patterns = Array.prototype.concat.apply([], patterns);
if (patterns.length === 0) {
return [];
}
patterns = Array.prototype.concat.apply([], patterns);

if (!options.nodir) {
for (let i = patterns.length - 1; i >= 0; i--) {
const idx = this._virBaseDir.indexOf(patterns[i]);
Expand All @@ -73,6 +83,16 @@ class AbstractAdapter extends AbstractReaderWriter {
});
}

/**
* Validate if virtual path should be excluded
*
* @param {string} virPath Virtual Path
* @returns {boolean} True if path is excluded, otherwise false
*/
isPathExcluded(virPath) {
return micromatch(virPath, this._excludes).length > 0;
}

/**
* Normalizes virtual glob patterns.
*
Expand Down
131 changes: 69 additions & 62 deletions lib/adapters/FileSystem.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const log = require("@ui5/logger").getLogger("resources:adapters:FileSystem");
const path = require("path");
const fs = require("graceful-fs");
const glob = require("globby");
const globby = require("globby");
const makeDir = require("make-dir");
const {PassThrough} = require("stream");
const Resource = require("../Resource");
Expand All @@ -21,9 +21,10 @@ class FileSystem extends AbstractAdapter {
* @param {Object} parameters Parameters
* @param {string} parameters.virBasePath Virtual base path
* @param {string} parameters.fsBasePath (Physical) File system path
* @param {string[]} [parameters.excludes] List of glob patterns to exclude
*/
constructor({virBasePath, project, fsBasePath}) {
super({virBasePath, project});
constructor({virBasePath, project, fsBasePath, excludes}) {
super({virBasePath, project, excludes});
this._fsBasePath = fsBasePath;
}

Expand All @@ -37,69 +38,71 @@ class FileSystem extends AbstractAdapter {
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources
*/
_runGlob(patterns, options = {nodir: true}, trace) {
return new Promise((resolve, reject) => {
const opt = {
cwd: this._fsBasePath,
dot: true,
nodir: options.nodir
};

trace.globCall();
glob(patterns, opt).then((matches) => {
const promises = [];
if (!opt.nodir && patterns[0] === "") { // Match physical root directory
promises.push(new Promise((resolve, reject) => {
fs.stat(this._fsBasePath, (err, stat) => {
if (err) {
reject(err);
} else {
resolve(new Resource({
project: this._project,
statInfo: stat,
path: this._virBaseDir,
createStream: () => {
return fs.createReadStream(this._fsBasePath);
}
}));
}
});
}));
}
async _runGlob(patterns, options = {nodir: true}, trace) {
const opt = {
cwd: this._fsBasePath,
dot: true,
onlyFiles: options.nodir,
followSymlinkedDirectories: false
};
trace.globCall();

for (let i = matches.length - 1; i >= 0; i--) {
promises.push(new Promise((resolve, reject) => {
const fsPath = path.join(this._fsBasePath, matches[i]);
const virPath = (this._virBasePath + matches[i]);

// Workaround for not getting the stat from the glob
fs.stat(fsPath, (err, stat) => {
if (err) {
reject(err);
} else {
resolve(new Resource({
project: this._project,
statInfo: stat,
path: virPath,
createStream: () => {
return fs.createReadStream(fsPath);
}
}));
const promises = [];
if (!opt.onlyFiles && patterns.includes("")) { // Match physical root directory
promises.push(new Promise((resolve, reject) => {
fs.stat(this._fsBasePath, (err, stat) => {
if (err) {
reject(err);
} else {
resolve(new Resource({
project: this._project,
statInfo: stat,
path: this._virBaseDir,
createStream: () => {
return fs.createReadStream(this._fsBasePath);
}
});
}));
}

Promise.all(promises).then(function(results) {
const flat = Array.prototype.concat.apply([], results);
resolve(flat);
}, function(err) {
reject(err);
}));
}
});
}).catch((err) => {
log.error(err);
});
}));
}

// Remove empty string glob patterns
// Starting with globby v8 or v9 empty glob patterns "" act like "**"
// Micromatch throws on empty strings. We just ignore them since they are
// typically caused by our normalization in the AbstractAdapter
const globbyPatterns = patterns.filter((pattern) => {
return pattern !== "";
});
if (globbyPatterns.length > 0) {
const matches = await globby(globbyPatterns, opt);
for (let i = matches.length - 1; i >= 0; i--) {
promises.push(new Promise((resolve, reject) => {
const fsPath = path.join(this._fsBasePath, matches[i]);
const virPath = (this._virBasePath + matches[i]);

// Workaround for not getting the stat from the glob
fs.stat(fsPath, (err, stat) => {
if (err) {
reject(err);
} else {
resolve(new Resource({
project: this._project,
statInfo: stat,
path: virPath,
createStream: () => {
return fs.createReadStream(fsPath);
}
}));
}
});
}));
}
}
const results = await Promise.all(promises);

// Flatten results
return Array.prototype.concat.apply([], results);
}

/**
Expand All @@ -112,6 +115,10 @@ class FileSystem extends AbstractAdapter {
* @returns {Promise<module:@ui5/fs.Resource>} Promise resolving to a single resource or null if not found
*/
_byPath(virPath, options, trace) {
if (this.isPathExcluded(virPath)) {
return Promise.resolve(null);
}

return new Promise((resolve, reject) => {
if (!virPath.startsWith(this._virBasePath) && virPath !== this._virBaseDir) {
// Neither starts with basePath, nor equals baseDirectory
Expand Down
16 changes: 10 additions & 6 deletions lib/adapters/Memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ class Memory extends AbstractAdapter {
* @public
* @param {Object} parameters Parameters
* @param {string} parameters.virBasePath Virtual base path
* @param {string[]} [parameters.excludes] List of glob patterns to exclude
*/
constructor({virBasePath, project}) {
super({virBasePath, project});
constructor({virBasePath, project, excludes}) {
super({virBasePath, project, excludes});
this._virFiles = {}; // map full of files
this._virDirs = {}; // map full of directories
}
Expand All @@ -34,9 +35,9 @@ class Memory extends AbstractAdapter {
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources
*/
_runGlob(patterns, options = {nodir: true}, trace) {
async _runGlob(patterns, options = {nodir: true}, trace) {
if (patterns[0] === "" && !options.nodir) { // Match virtual root directory
return Promise.resolve([
return [
new Resource({
project: this.project,
statInfo: { // TODO: make closer to fs stat info
Expand All @@ -46,7 +47,7 @@ class Memory extends AbstractAdapter {
},
path: this._virBasePath.slice(0, -1)
})
]);
];
}

const filePaths = Object.keys(this._virFiles);
Expand All @@ -67,7 +68,7 @@ class Memory extends AbstractAdapter {
}));
}

return Promise.resolve(matchedResources);
return matchedResources;
}

/**
Expand All @@ -80,6 +81,9 @@ class Memory extends AbstractAdapter {
* @returns {Promise<module:@ui5/fs.Resource>} Promise resolving to a single resource
*/
_byPath(virPath, options, trace) {
if (this.isPathExcluded(virPath)) {
return Promise.resolve(null);
}
return new Promise((resolve, reject) => {
if (!virPath.startsWith(this._virBasePath) && virPath !== this._virBaseDir) {
// Neither starts with basePath, nor equals baseDirectory
Expand Down
Loading