diff --git a/.gitignore b/.gitignore
index 052409d7..a5e3a601 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,9 @@ build/Release
node_modules
jspm_packages
+# TypeScript cache
+*.tsbuildinfo
+
# Optional npm cache directory
.npm
@@ -60,3 +63,7 @@ deploy_key
# Custom directories
test/tmp/
jsdocs/
+
+# Generated code
+lib/
+!test/lib/
diff --git a/.nycrc.json b/.nycrc.json
new file mode 100644
index 00000000..195a297e
--- /dev/null
+++ b/.nycrc.json
@@ -0,0 +1,30 @@
+{
+ "extends": "@istanbuljs/nyc-config-typescript",
+ "reporter": ["lcov", "text", "text-summary"],
+ "include": ["src/**"],
+ "check-coverage": true,
+ "statements": 90,
+ "branches": 85,
+ "functions": 90,
+ "lines": 90,
+ "watermarks": {
+ "statements": [
+ 70,
+ 90
+ ],
+ "branches": [
+ 70,
+ 90
+ ],
+ "functions": [
+ 70,
+ 90
+ ],
+ "lines": [
+ 70,
+ 90
+ ]
+ },
+ "cache": true,
+ "all": true
+}
\ No newline at end of file
diff --git a/ava.config.js b/ava.config.js
new file mode 100644
index 00000000..a256173d
--- /dev/null
+++ b/ava.config.js
@@ -0,0 +1,21 @@
+// Calculate nodeArguments based on the Node version
+const nodeArguments = [
+ "--import=tsx/esm",
+ "--no-warnings=ExperimentalWarning",
+];
+
+export default {
+ extensions: {
+ ts: "module",
+ },
+ files: [
+ "test/lib/**/*.ts",
+ ],
+ watchMode: {
+ ignoreChanges: [
+ "test/tmp/**",
+ ],
+ },
+ nodeArguments,
+ workerThreads: false,
+};
diff --git a/eslint.common.config.js b/eslint.common.config.js
index 07876d52..f6284f68 100644
--- a/eslint.common.config.js
+++ b/eslint.common.config.js
@@ -1,99 +1,147 @@
-import jsdoc from "eslint-plugin-jsdoc";
+import eslint from "@eslint/js";
+import tseslint from "typescript-eslint";
+import stylistic from "@stylistic/eslint-plugin";
import ava from "eslint-plugin-ava";
-import globals from "globals";
-import js from "@eslint/js";
-import google from "eslint-config-google";
-
-export default [{
- ignores: [ // Common ignore patterns across all tooling repos
- "**/coverage/",
- "test/tmp/",
- "test/expected/",
- "test/fixtures/",
- "**/docs/",
- "**/jsdocs/",
- ],
-}, js.configs.recommended, google, ava.configs["flat/recommended"], {
- name: "Common ESLint config used for all tooling repos",
+import jsdoc from "eslint-plugin-jsdoc";
- plugins: {
- jsdoc,
- },
+export default tseslint.config(
+ {
+ // This block defines ignore patterns globally to all configurations below
+ // (therefore it can use slightly different patterns, see also the eslint "Flat Config" doc)
+ ignores: [
+ ".github/*",
+ ".reuse/*",
+ "coverage/*",
+ "**/docs/",
+ "**/jsdocs/",
- languageOptions: {
- globals: {
- ...globals.node,
- },
+ // Exclude test files
+ "test/tmp/*",
+ "test/fixtures/*",
+ "test/expected/*",
- ecmaVersion: 2023,
- sourceType: "module",
+ // Exclude generated code
+ "lib/*",
+ ],
},
-
- settings: {
- jsdoc: {
- mode: "jsdoc",
-
- tagNamePreference: {
- return: "returns",
- augments: "extends",
+ // Base configs applying to JS and TS files
+ eslint.configs.recommended,
+ stylistic.configs.customize({
+ indent: "tab",
+ quotes: "double",
+ semi: true,
+ jsx: false,
+ arrowParens: true,
+ braceStyle: "1tbs",
+ blockSpacing: false,
+ }),
+ ava.configs["flat/recommended"], {
+ // Lint all JS files using the eslint parser
+ files: ["**/*.js"],
+ languageOptions: {
+ ecmaVersion: 2022,
+ sourceType: "module",
+ },
+ }, {
+ // Lint all TS files using the typescript-eslint parser
+ // Also enable all recommended typescript-eslint rules
+ files: ["src/**/*.ts", "test/**/*.ts", "scripts/**/*.ts"],
+ extends: [
+ ...tseslint.configs.recommendedTypeChecked,
+ ...tseslint.configs.stylisticTypeChecked,
+ ],
+ languageOptions: {
+ ecmaVersion: 2022,
+ sourceType: "module",
+ parser: tseslint.parser,
+ parserOptions: {
+ project: true,
},
},
- },
-
- rules: {
- "indent": ["error", "tab"],
- "linebreak-style": ["error", "unix"],
-
- "quotes": ["error", "double", {
- allowTemplateLiterals: true,
- }],
-
- "semi": ["error", "always"],
- "no-negated-condition": "off",
- "require-jsdoc": "off",
- "no-mixed-requires": "off",
-
- "max-len": ["error", {
- code: 120,
- ignoreUrls: true,
- ignoreRegExpLiterals: true,
- }],
-
- "no-implicit-coercion": [2, {
- allow: ["!!"],
- }],
-
- "comma-dangle": "off",
- "no-tabs": "off",
- "no-console": 2, // Disallow console.log()
- "no-eval": 2,
- // The following rule must be disabled as of ESLint 9.
- // It's removed and causes issues when present
- // https://eslint.org/docs/latest/rules/valid-jsdoc
- "valid-jsdoc": 0,
- "jsdoc/check-examples": 0,
- "jsdoc/check-param-names": 2,
- "jsdoc/check-tag-names": 2,
- "jsdoc/check-types": 2,
- "jsdoc/no-undefined-types": 0,
- "jsdoc/require-description": 0,
- "jsdoc/require-description-complete-sentence": 0,
- "jsdoc/require-example": 0,
- "jsdoc/require-hyphen-before-param-description": 0,
- "jsdoc/require-param": 2,
- "jsdoc/require-param-description": 0,
- "jsdoc/require-param-name": 2,
- "jsdoc/require-param-type": 2,
- "jsdoc/require-returns": 0,
- "jsdoc/require-returns-description": 0,
- "jsdoc/require-returns-type": 2,
-
- "jsdoc/tag-lines": [2, "any", {
- startLines: 1,
- }],
+ rules: {
+ // TypeScript specific overwrites
+ // We must disable the base rule as it can report incorrect errors
+ "no-unused-vars": "off",
+ "@typescript-eslint/consistent-type-imports": ["error", {
+ fixStyle: "inline-type-imports",
+ }],
+ "@typescript-eslint/consistent-type-exports": ["error", {
+ fixMixedExportsWithInlineTypeSpecifier: true,
+ }],
+ "@typescript-eslint/no-unused-vars": [
+ "error", {
+ argsIgnorePattern: "^_",
+ varsIgnorePattern: "^_",
+ caughtErrorsIgnorePattern: "^_",
+ },
+ ],
+ },
+ }, {
+ // To be discussed: Type-aware checks might add quite some additional work when writing tests
+ // and could even require us to export types that we would otherwise not export
+ files: ["test/**/*.ts"],
+ rules: {
+ "@typescript-eslint/no-unsafe-argument": "off",
+ "@typescript-eslint/no-unsafe-assignment": "off",
+ "@typescript-eslint/no-unsafe-call": "off",
+ "@typescript-eslint/no-unsafe-enum-comparison": "off",
+ "@typescript-eslint/no-unsafe-member-access": "off",
+ "@typescript-eslint/no-unsafe-return": "off",
+ "@typescript-eslint/no-unsafe-unary-minus": "off",
+ },
+ }, {
+ // Overwrite any rules from the configurations above for both, JS and TS files
+ rules: {
+ "linebreak-style": [
+ "error",
+ "unix",
+ ],
+ "@stylistic/object-curly-spacing": [
+ "error",
+ "never",
+ ],
+ "@stylistic/operator-linebreak": ["error", "after"],
+ "@stylistic/comma-dangle": ["error", {
+ functions: "never",
+ arrays: "always-multiline",
+ objects: "always-multiline",
+ imports: "always-multiline",
+ exports: "always-multiline",
+ enums: "always-multiline",
+ generics: "always-multiline",
+ tuples: "always-multiline",
+ }],
+ "max-len": [
+ "error",
+ {
+ code: 120,
+ ignoreUrls: true,
+ ignoreRegExpLiterals: true,
+ },
+ ],
+ "no-implicit-coercion": [
+ "error",
+ {allow: ["!!"]},
+ ],
+ "no-console": "error",
+ "no-eval": "error",
- "jsdoc/valid-types": 0,
- "ava/assertion-arguments": 0,
- },
-}
-];
+ "valid-jsdoc": "off",
+ },
+ }, {
+ // JSdoc only applying to sources
+ files: ["src/**/*.ts"],
+ ...jsdoc.configs["flat/recommended-typescript-error"],
+ }, {
+ // Overwriting JSDoc rules in a separate config with the same files pattern
+ files: ["src/**/*.ts"],
+ rules: {
+ "jsdoc/require-jsdoc": "off",
+ "jsdoc/require-returns": "off",
+ "jsdoc/require-returns-description": "off",
+ "jsdoc/tag-lines": ["error", "any", {
+ startLines: 1,
+ }],
+ },
+ }
+);
diff --git a/eslint.config.js b/eslint.config.js
index f5873aa9..e87b98da 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -2,4 +2,8 @@ import eslintCommonConfig from "./eslint.common.config.js";
export default [
...eslintCommonConfig, // Load common ESLint config
+ {
+ // Add project-specific ESLint config rules here
+ // in order to override common config
+ },
];
diff --git a/jsdoc-plugin.cjs b/jsdoc-plugin.cjs
index cd7ef446..4305acfe 100644
--- a/jsdoc-plugin.cjs
+++ b/jsdoc-plugin.cjs
@@ -3,7 +3,7 @@
* JSDoc doesn't see "{@" as a valid type expression, probably as there's also {@link ...}.
*/
exports.handlers = {
- jsdocCommentFound: function(e) {
+ jsdocCommentFound: function (e) {
e.comment = e.comment.replace(/{@ui5\//g, "{ @ui5/");
- }
+ },
};
diff --git a/lib/AbstractReader.js b/lib/AbstractReader.js
deleted file mode 100644
index 8f5c3632..00000000
--- a/lib/AbstractReader.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import randomInt from "random-int";
-import Trace from "./tracing/Trace.js";
-
-/**
- * Abstract resource locator implementing the general API for reading resources
- *
- * @abstract
- * @public
- * @class
- * @alias @ui5/fs/AbstractReader
- */
-class AbstractReader {
- /**
- * The constructor.
- *
- * @public
- * @param {string} name Name of the reader. Typically used for tracing purposes
- */
- constructor(name) {
- if (new.target === AbstractReader) {
- throw new TypeError("Class 'AbstractReader' is abstract");
- }
- this._name = name;
- }
-
- /*
- * Returns the name of the reader instance. This can be used for logging/tracing purposes.
- *
- * @returns {string} Name of the reader
- */
- getName() {
- return this._name || ``;
- }
-
- /**
- * Locates resources by matching glob patterns.
- *
- * @example
- * byGlob("**/*.{html,htm}");
- * byGlob("**/.library");
- * byGlob("/pony/*");
- *
- * @public
- * @param {string|string[]} virPattern glob pattern as string or array of glob patterns for
- * virtual directory structure
- * @param {object} [options] glob options
- * @param {boolean} [options.nodir=true] Do not match directories
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- byGlob(virPattern, options = {nodir: true}) {
- const trace = new Trace(virPattern);
- return this._byGlob(virPattern, options, trace).then(function(result) {
- trace.printReport();
- return result;
- }).then((resources) => {
- if (resources.length > 1) {
- // Pseudo randomize result order to prevent consumers from relying on it:
- // Swap the first object with a randomly chosen one
- const x = 0;
- const y = randomInt(0, resources.length - 1);
- // Swap object at index "x" with "y"
- resources[x] = [resources[y], resources[y]=resources[x]][0];
- }
- return resources;
- });
- }
-
- /**
- * Locates resources by matching a given path.
- *
- * @public
- * @param {string} virPath Virtual path
- * @param {object} [options] Options
- * @param {boolean} [options.nodir=true] Do not match directories
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
- */
- byPath(virPath, options = {nodir: true}) {
- const trace = new Trace(virPath);
- return this._byPath(virPath, options, trace).then(function(resource) {
- trace.printReport();
- return resource;
- });
- }
-
- /**
- * Locates resources by one or more glob patterns.
- *
- * @abstract
- * @protected
- * @param {string|string[]} virPattern glob pattern as string or an array of
- * glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- _byGlob(virPattern, options, trace) {
- throw new Error("Function '_byGlob' is not implemented");
- }
-
- /**
- * Locate resources by matching a single glob pattern.
- *
- * @abstract
- * @protected
- * @param {string} pattern glob pattern
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- _runGlob(pattern, options, trace) {
- throw new Error("Function '_runGlob' is not implemented");
- }
-
- /**
- * Locates resources by path.
- *
- * @abstract
- * @protected
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
- */
- _byPath(virPath, options, trace) {
- throw new Error("Function '_byPath' is not implemented");
- }
-}
-
-export default AbstractReader;
diff --git a/lib/AbstractReaderWriter.js b/lib/AbstractReaderWriter.js
deleted file mode 100644
index 9e07da5e..00000000
--- a/lib/AbstractReaderWriter.js
+++ /dev/null
@@ -1,71 +0,0 @@
-import AbstractReader from "./AbstractReader.js";
-
-/**
- * Abstract resource locator implementing the general API for reading and writing resources
- *
- * @abstract
- * @public
- * @class
- * @alias @ui5/fs/AbstractReaderWriter
- * @extends @ui5/fs/AbstractReader
- */
-class AbstractReaderWriter extends AbstractReader {
- /**
- * The constructor.
- *
- * @public
- * @param {string} name Name of the reader/writer. Typically used for tracing purposes
- */
- constructor(name) {
- if (new.target === AbstractReaderWriter) {
- throw new TypeError("Class 'AbstractReaderWriter' is abstract");
- }
- super(name);
- }
-
- /*
- * Returns the name of the reader/writer instance. This can be used for logging/tracing purposes.
- *
- * @returns {string} Name of the reader/writer
- */
- getName() {
- return this._name || ``;
- }
-
- /**
- * Writes the content of a resource to a path.
- *
- * @public
- * @param {@ui5/fs/Resource} resource Resource to write
- * @param {object} [options]
- * @param {boolean} [options.readOnly=false] Whether the resource content shall be written read-only
- * Do not use in conjunction with the drain
option.
- * The written file will be used as the new source of this resources content.
- * Therefore the written file should not be altered by any means.
- * Activating this option might improve overall memory consumption.
- * @param {boolean} [options.drain=false] Whether the resource content shall be emptied during the write process.
- * Do not use in conjunction with the readOnly
option.
- * Activating this option might improve overall memory consumption.
- * This should be used in cases where this is the last access to the resource.
- * E.g. the final write of a resource after all processing is finished.
- * @returns {Promise} Promise resolving once data has been written
- */
- write(resource, options = {drain: false, readOnly: false}) {
- return this._write(resource, options);
- }
-
- /**
- * Writes the content of a resource to a path.
- *
- * @abstract
- * @protected
- * @param {@ui5/fs/Resource} resource Resource to write
- * @param {object} [options] Write options, see above
- * @returns {Promise} Promise resolving once data has been written
- */
- _write(resource, options) {
- throw new Error("Not implemented");
- }
-}
-
-export default AbstractReaderWriter;
diff --git a/lib/ReaderCollection.js b/lib/ReaderCollection.js
deleted file mode 100644
index f0fa9ffc..00000000
--- a/lib/ReaderCollection.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import AbstractReader from "./AbstractReader.js";
-
-/**
- * Resource Locator ReaderCollection
- *
- * @public
- * @class
- * @alias @ui5/fs/ReaderCollection
- * @extends @ui5/fs/AbstractReader
- */
-class ReaderCollection extends AbstractReader {
- /**
- * The constructor.
- *
- * @param {object} parameters Parameters
- * @param {string} parameters.name The collection name
- * @param {@ui5/fs/AbstractReader[]} [parameters.readers]
- * List of resource readers (all tried in parallel).
- * If none are provided, the collection will never return any results.
- */
- constructor({name, readers}) {
- super(name);
-
- // Remove any undefined (empty) readers from array
- this._readers = readers.filter(($) => $);
- }
-
- /**
- * Locates resources by glob.
- *
- * @private
- * @param {string|string[]} pattern glob pattern as string or an array of
- * glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- _byGlob(pattern, options, trace) {
- return Promise.all(this._readers.map(function(resourceLocator) {
- return resourceLocator._byGlob(pattern, options, trace);
- })).then((result) => {
- trace.collection(this._name);
- return Array.prototype.concat.apply([], result);
- });
- }
-
- /**
- * Locates resources by path.
- *
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource|null>}
- * Promise resolving to a single resource or null
if no resource is found
- */
- _byPath(virPath, options, trace) {
- const that = this;
- const resourceLocatorCount = this._readers.length;
- let resolveCount = 0;
-
- if (resourceLocatorCount === 0) {
- // Short-circuit if there are no readers (Promise.race does not settle for empty arrays)
- trace.collection(that._name);
- return Promise.resolve(null);
- }
-
- // Using Promise.race to deliver files that can be found as fast as possible
- return Promise.race(this._readers.map(function(resourceLocator) {
- return resourceLocator._byPath(virPath, options, trace).then(function(resource) {
- return new Promise(function(resolve, reject) {
- trace.collection(that._name);
- resolveCount++;
- if (resource) {
- resource.pushCollection(that._name);
- resolve(resource);
- } else if (resolveCount === resourceLocatorCount) {
- resolve(null);
- }
- });
- });
- }));
- }
-}
-
-export default ReaderCollection;
diff --git a/lib/ReaderCollectionPrioritized.js b/lib/ReaderCollectionPrioritized.js
deleted file mode 100644
index 680b7135..00000000
--- a/lib/ReaderCollectionPrioritized.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import AbstractReader from "./AbstractReader.js";
-
-/**
- * Prioritized Resource Locator Collection
- *
- * @public
- * @class
- * @alias @ui5/fs/ReaderCollectionPrioritized
- * @extends @ui5/fs/AbstractReader
- */
-class ReaderCollectionPrioritized extends AbstractReader {
- /**
- * The constructor.
- *
- * @param {object} parameters
- * @param {string} parameters.name The collection name
- * @param {@ui5/fs/AbstractReader[]} [parameters.readers]
- * Prioritized list of resource readers (tried in the order provided).
- * If none are provided, the collection will never return any results.
- */
- constructor({readers, name}) {
- super(name);
-
- // Remove any undefined (empty) readers from array
- this._readers = readers.filter(($) => $);
- }
-
- /**
- * Locates resources by glob.
- *
- * @private
- * @param {string|string[]} pattern glob pattern as string or an array of
- * glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- _byGlob(pattern, options, trace) {
- return Promise.all(this._readers.map(function(resourceLocator) {
- return resourceLocator._byGlob(pattern, options, trace);
- })).then((result) => {
- const files = Object.create(null);
- const resources = [];
- // Prefer files found in preceding resource locators
- for (let i = 0; i < result.length; i++) {
- for (let j = 0; j < result[i].length; j++) {
- const resource = result[i][j];
- const path = resource.getPath();
- if (!files[path]) {
- files[path] = true;
- resources.push(resource);
- }
- }
- }
-
- trace.collection(this._name);
- return resources;
- });
- }
-
- /**
- * Locates resources by path.
- *
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource|null>}
- * Promise resolving to a single resource or null
if no resource is found
- */
- _byPath(virPath, options, trace) {
- const that = this;
- const byPath = (i) => {
- if (i > this._readers.length - 1) {
- return null;
- }
- return this._readers[i]._byPath(virPath, options, trace).then((resource) => {
- if (resource) {
- resource.pushCollection(that._name);
- return resource;
- } else {
- return byPath(++i);
- }
- });
- };
- return byPath(0);
- }
-}
-
-export default ReaderCollectionPrioritized;
diff --git a/lib/fsInterface.js b/lib/fsInterface.js
deleted file mode 100644
index 99d97712..00000000
--- a/lib/fsInterface.js
+++ /dev/null
@@ -1,92 +0,0 @@
-function toPosix(inputPath) {
- return inputPath.replace(/\\/g, "/");
-}
-
-/**
- * @public
- * @module @ui5/fs/fsInterface
- */
-
-/**
- * Wraps readers to access them through a [Node.js fs]{@link https://nodejs.org/api/fs.html} styled interface.
- *
- * @public
- * @function default
- * @static
- * @param {@ui5/fs/AbstractReader} reader Resource Reader or Collection
- *
- * @returns {object} Object with [Node.js fs]{@link https://nodejs.org/api/fs.html} styled functions
- * [readFile
]{@link https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback},
- * [stat
]{@link https://nodejs.org/api/fs.html#fs_fs_stat_path_options_callback},
- * [readdir
]{@link https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback} and
- * [mkdir
]{@link https://nodejs.org/api/fs.html#fs_fs_mkdir_path_options_callback}
- */
-function fsInterface(reader) {
- return {
- readFile(fsPath, options, callback) {
- if (typeof options === "function") {
- callback = options;
- options = undefined;
- }
- if (typeof options === "string") {
- options = {encoding: options};
- }
- const posixPath = toPosix(fsPath);
- reader.byPath(posixPath, {
- nodir: false
- }).then(function(resource) {
- if (!resource) {
- const error = new Error(`ENOENT: no such file or directory, open '${fsPath}'`);
- error.code = "ENOENT"; // "File or directory does not exist"
- callback(error);
- return;
- }
-
- return resource.getBuffer().then(function(buffer) {
- let res;
-
- if (options && options.encoding) {
- res = buffer.toString(options.encoding);
- } else {
- res = buffer;
- }
-
- callback(null, res);
- });
- }).catch(callback);
- },
- stat(fsPath, callback) {
- const posixPath = toPosix(fsPath);
- reader.byPath(posixPath, {
- nodir: false
- }).then(function(resource) {
- if (!resource) {
- const error = new Error(`ENOENT: no such file or directory, stat '${fsPath}'`);
- error.code = "ENOENT"; // "File or directory does not exist"
- callback(error);
- } else {
- callback(null, resource.getStatInfo());
- }
- }).catch(callback);
- },
- readdir(fsPath, callback) {
- let posixPath = toPosix(fsPath);
- if (!posixPath.match(/\/$/)) {
- // Add trailing slash if not present
- posixPath += "/";
- }
- reader.byGlob(posixPath + "*", {
- nodir: false
- }).then((resources) => {
- const files = resources.map((resource) => {
- return resource.getName();
- });
- callback(null, files);
- }).catch(callback);
- },
- mkdir(fsPath, callback) {
- setTimeout(callback, 0);
- }
- };
-}
-export default fsInterface;
diff --git a/lib/readers/Filter.js b/lib/readers/Filter.js
deleted file mode 100644
index b95654da..00000000
--- a/lib/readers/Filter.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import AbstractReader from "../AbstractReader.js";
-
-/**
- * A reader that allows dynamic filtering of resources passed through it
- *
- * @public
- * @class
- * @alias @ui5/fs/readers/Filter
- * @extends @ui5/fs/AbstractReader
- */
-class Filter extends AbstractReader {
- /**
- * Filter callback
- *
- * @public
- * @callback @ui5/fs/readers/Filter~callback
- * @param {@ui5/fs/Resource} resource Resource to test
- * @returns {boolean} Whether to keep the resource
- */
-
- /**
- * Constructor
- *
- * @public
- * @param {object} parameters Parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader The resource reader or collection to wrap
- * @param {@ui5/fs/readers/Filter~callback} parameters.callback
- * Filter function. Will be called for every resource read through this reader.
- */
- constructor({reader, callback}) {
- super();
- if (!reader) {
- throw new Error(`Missing parameter "reader"`);
- }
- if (!callback) {
- throw new Error(`Missing parameter "callback"`);
- }
- this._reader = reader;
- this._callback = callback;
- }
-
- /**
- * Locates resources by glob.
- *
- * @private
- * @param {string|string[]} pattern glob pattern as string or an array of
- * glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing/Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
- */
- async _byGlob(pattern, options, trace) {
- const result = await this._reader._byGlob(pattern, options, trace);
- return result.filter(this._callback);
- }
-
- /**
- * Locates resources by path.
- *
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing/Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
- */
- async _byPath(virPath, options, trace) {
- const result = await this._reader._byPath(virPath, options, trace);
- if (result && !this._callback(result)) {
- return null;
- }
- return result;
- }
-}
-
-export default Filter;
diff --git a/package-lock.json b/package-lock.json
index 7ee84c93..6928a1ca 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,15 +20,21 @@
"random-int": "^3.0.0"
},
"devDependencies": {
- "@eslint/js": "^9.8.0",
+ "@eslint/js": "^9.9.1",
"@istanbuljs/esm-loader-hook": "^0.2.0",
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
+ "@stylistic/eslint-plugin": "^2.6.4",
+ "@types/clone": "^2.1.4",
+ "@types/micromatch": "^4.0.9",
+ "@types/node": "^20.11.0",
+ "@types/pretty-hrtime": "^1.0.3",
+ "@types/sinon": "^17.0.3",
"ava": "^6.1.3",
"chokidar-cli": "^3.0.0",
"cross-env": "^7.0.3",
"depcheck": "^1.4.7",
"docdash": "^2.0.2",
- "eslint": "^9.9.1",
- "eslint-config-google": "^0.14.0",
+ "eslint": "^9.9.0",
"eslint-plugin-ava": "^15.0.1",
"eslint-plugin-jsdoc": "^50.2.2",
"esmock": "^2.6.7",
@@ -38,7 +44,11 @@
"open-cli": "^8.0.0",
"rimraf": "^6.0.1",
"sinon": "^18.0.0",
- "tap-xunit": "^2.4.1"
+ "tap-xunit": "^2.4.1",
+ "tsx": "^4.17.0",
+ "typedoc": "^0.26.6",
+ "typedoc-plugin-rename-defaults": "^0.7.1",
+ "typescript-eslint": "^8.2.0"
},
"engines": {
"node": "^20.11.0 || >=22.0.0",
@@ -333,6 +343,414 @@
"node": ">=16"
}
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+ "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+ "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+ "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+ "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+ "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+ "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+ "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+ "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+ "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+ "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+ "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+ "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+ "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+ "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+ "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+ "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+ "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+ "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+ "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+ "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -613,6 +1031,22 @@
"node": ">=8"
}
},
+ "node_modules/@istanbuljs/nyc-config-typescript": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz",
+ "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "nyc": ">=15"
+ }
+ },
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@@ -797,113 +1231,559 @@
"node": ">= 8.0.0"
}
},
- "node_modules/@rollup/pluginutils/node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@shikijs/core": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.14.1.tgz",
+ "integrity": "sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.4"
+ }
+ },
+ "node_modules/@sindresorhus/merge-streams": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
+ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@sinonjs/commons": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
+ "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/fake-timers": {
+ "version": "11.3.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz",
+ "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==",
+ "dev": true,
+ "dependencies": {
+ "@sinonjs/commons": "^3.0.1"
+ }
+ },
+ "node_modules/@sinonjs/samsam": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
+ "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
+ "dev": true,
+ "dependencies": {
+ "@sinonjs/commons": "^2.0.0",
+ "lodash.get": "^4.4.2",
+ "type-detect": "^4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+ "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "node_modules/@sinonjs/text-encoding": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz",
+ "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==",
+ "dev": true
+ },
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.6.4.tgz",
+ "integrity": "sha512-euUGnjzH8EOqEYTGk9dB2OBINp0FX1nuO7/k4fO82zNRBIKZgJoDwTLM4Ce+Om6W1Qmh1PrZjCr4jh4tMEXGPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.6.4",
+ "@stylistic/eslint-plugin-jsx": "2.6.4",
+ "@stylistic/eslint-plugin-plus": "2.6.4",
+ "@stylistic/eslint-plugin-ts": "2.6.4",
+ "@types/eslint": "^9.6.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-js": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.6.4.tgz",
+ "integrity": "sha512-kx1hS3xTvzxZLdr/DCU/dLBE++vcP97sHeEFX2QXhk1Ipa4K1rzPOLw1HCbf4mU3s+7kHP5eYpDe+QteEOFLug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint": "^9.6.0",
+ "acorn": "^8.12.1",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-jsx": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.6.4.tgz",
+ "integrity": "sha512-bIvVhdtjmyu3S10V7QRIuawtCZSq9gRmzAX23ucjCOdSFzEwlq+di0IM0riBAvvQerrJL4SM6G3xgyPs8BSXIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "^2.6.4",
+ "@types/eslint": "^9.6.0",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-jsx/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.6.4.tgz",
+ "integrity": "sha512-EuRvtxhf7Hv8OoMIePulP/6rBJIgPTu1l5GAm1780WcF1Cl8bOZXIn84Pdac5pNv6lVlzCOFm8MD3VE+2YROuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint": "^9.6.0"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.6.4.tgz",
+ "integrity": "sha512-yxL8Hj6WkObw1jfiLpBzKy5yfxY6vwlwO4miq34ySErUjUecPV5jxfVbOe4q1QDPKemQGPq93v7sAQS5PzM8lA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.6.4",
+ "@types/eslint": "^9.6.0",
+ "@typescript-eslint/utils": "^8.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+ "dev": true
+ },
+ "node_modules/@types/braces": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.4.tgz",
+ "integrity": "sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/clone": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.4.tgz",
+ "integrity": "sha512-NKRWaEGaVGVLnGLB2GazvDaZnyweW9FJLLFL5LhywGJB3aqGMT9R/EUoJoSRP4nzofYnZysuDmrEJtJdAqUOtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "dev": true
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+ "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "dev": true,
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "dev": true
+ },
+ "node_modules/@types/micromatch": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.9.tgz",
+ "integrity": "sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/braces": "*"
+ }
+ },
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "20.16.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz",
+ "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+ "dev": true
+ },
+ "node_modules/@types/pretty-hrtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+ "integrity": "sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/sinon": {
+ "version": "17.0.3",
+ "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz",
+ "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/sinonjs__fake-timers": "*"
+ }
+ },
+ "node_modules/@types/sinonjs__fake-timers": {
+ "version": "8.1.5",
+ "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz",
+ "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz",
+ "integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.3.0",
+ "@typescript-eslint/type-utils": "8.3.0",
+ "@typescript-eslint/utils": "8.3.0",
+ "@typescript-eslint/visitor-keys": "8.3.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz",
+ "integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.3.0",
+ "@typescript-eslint/types": "8.3.0",
+ "@typescript-eslint/typescript-estree": "8.3.0",
+ "@typescript-eslint/visitor-keys": "8.3.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz",
+ "integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.3.0",
+ "@typescript-eslint/visitor-keys": "8.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz",
+ "integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.3.0",
+ "@typescript-eslint/utils": "8.3.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz",
+ "integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@sindresorhus/merge-streams": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
- "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz",
+ "integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.3.0",
+ "@typescript-eslint/visitor-keys": "8.3.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@sinonjs/commons": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
- "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
+ "license": "ISC",
"dependencies": {
- "type-detect": "4.0.8"
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@sinonjs/fake-timers": {
- "version": "11.3.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz",
- "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true,
- "dependencies": {
- "@sinonjs/commons": "^3.0.1"
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "node_modules/@sinonjs/samsam": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
- "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz",
+ "integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@sinonjs/commons": "^2.0.0",
- "lodash.get": "^4.4.2",
- "type-detect": "^4.0.8"
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.3.0",
+ "@typescript-eslint/types": "8.3.0",
+ "@typescript-eslint/typescript-estree": "8.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
- "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
- "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz",
+ "integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "type-detect": "4.0.8"
+ "@typescript-eslint/types": "8.3.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@sinonjs/text-encoding": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz",
- "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==",
- "dev": true
- },
- "node_modules/@tokenizer/token": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
- "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
- "dev": true
- },
- "node_modules/@types/linkify-it": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
- "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
- "dev": true
- },
- "node_modules/@types/markdown-it": {
- "version": "14.1.2",
- "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
- "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
- "dependencies": {
- "@types/linkify-it": "^5",
- "@types/mdurl": "^2"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/@types/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
- "dev": true
- },
- "node_modules/@types/minimatch": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
- "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
- "dev": true
- },
- "node_modules/@types/parse-json": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
- "dev": true
- },
"node_modules/@ui5/logger": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@ui5/logger/-/logger-4.0.1.tgz",
@@ -2521,6 +3401,46 @@
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true
},
+ "node_modules/esbuild": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+ "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.23.1",
+ "@esbuild/android-arm": "0.23.1",
+ "@esbuild/android-arm64": "0.23.1",
+ "@esbuild/android-x64": "0.23.1",
+ "@esbuild/darwin-arm64": "0.23.1",
+ "@esbuild/darwin-x64": "0.23.1",
+ "@esbuild/freebsd-arm64": "0.23.1",
+ "@esbuild/freebsd-x64": "0.23.1",
+ "@esbuild/linux-arm": "0.23.1",
+ "@esbuild/linux-arm64": "0.23.1",
+ "@esbuild/linux-ia32": "0.23.1",
+ "@esbuild/linux-loong64": "0.23.1",
+ "@esbuild/linux-mips64el": "0.23.1",
+ "@esbuild/linux-ppc64": "0.23.1",
+ "@esbuild/linux-riscv64": "0.23.1",
+ "@esbuild/linux-s390x": "0.23.1",
+ "@esbuild/linux-x64": "0.23.1",
+ "@esbuild/netbsd-x64": "0.23.1",
+ "@esbuild/openbsd-arm64": "0.23.1",
+ "@esbuild/openbsd-x64": "0.23.1",
+ "@esbuild/sunos-x64": "0.23.1",
+ "@esbuild/win32-arm64": "0.23.1",
+ "@esbuild/win32-ia32": "0.23.1",
+ "@esbuild/win32-x64": "0.23.1"
+ }
+ },
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
@@ -2600,18 +3520,6 @@
}
}
},
- "node_modules/eslint-config-google": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz",
- "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- },
- "peerDependencies": {
- "eslint": ">=5.16.0"
- }
- },
"node_modules/eslint-plugin-ava": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-15.0.1.tgz",
@@ -3456,6 +4364,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/get-tsconfig": {
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz",
+ "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -3599,6 +4520,13 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -4436,6 +5364,13 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lunr": {
+ "version": "2.3.9",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
+ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/magic-string": {
"version": "0.30.11",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
@@ -5816,6 +6751,16 @@
"node": ">=8"
}
},
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -6027,6 +6972,17 @@
"node": ">=8"
}
},
+ "node_modules/shiki": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.14.1.tgz",
+ "integrity": "sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@shikijs/core": "1.14.1",
+ "@types/hast": "^3.0.4"
+ }
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6705,12 +7661,45 @@
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true
},
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
"dev": true
},
+ "node_modules/tsx": {
+ "version": "4.19.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz",
+ "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.23.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -6750,6 +7739,123 @@
"is-typedarray": "^1.0.0"
}
},
+ "node_modules/typedoc": {
+ "version": "0.26.6",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.6.tgz",
+ "integrity": "sha512-SfEU3SH3wHNaxhFPjaZE2kNl/NFtLNW5c1oHsg7mti7GjmUj1Roq6osBQeMd+F4kL0BoRBBr8gQAuqBlfFu8LA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lunr": "^2.3.9",
+ "markdown-it": "^14.1.0",
+ "minimatch": "^9.0.5",
+ "shiki": "^1.9.1",
+ "yaml": "^2.4.5"
+ },
+ "bin": {
+ "typedoc": "bin/typedoc"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x"
+ }
+ },
+ "node_modules/typedoc-plugin-rename-defaults": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.7.1.tgz",
+ "integrity": "sha512-hgg4mAy5IumgUmPOnVVGmGywjTGtUCmRJ2jRbseqtXdlUuYKj652ODL9joUWFt5uvNu4Dr/pNILc/qsKGHJw+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase": "^8.0.0"
+ },
+ "peerDependencies": {
+ "typedoc": ">=0.22.x <0.27.x"
+ }
+ },
+ "node_modules/typedoc-plugin-rename-defaults/node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typedoc/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typedoc/node_modules/yaml": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
+ "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+ "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.3.0.tgz",
+ "integrity": "sha512-EvWjwWLwwKDIJuBjk2I6UkV8KEQcwZ0VM10nR1rIunRDIP67QJTZAHBXTX0HW/oI1H10YESF8yWie8fRQxjvFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.3.0",
+ "@typescript-eslint/parser": "8.3.0",
+ "@typescript-eslint/utils": "8.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
@@ -6762,6 +7868,13 @@
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
"dev": true
},
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/unicorn-magic": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
diff --git a/package.json b/package.json
index 06998ddd..4b643463 100644
--- a/package.json
+++ b/package.json
@@ -18,24 +18,28 @@
],
"type": "module",
"exports": {
- "./adapters/*": "./lib/adapters/*.js",
- "./AbstractReader": "./lib/AbstractReader.js",
- "./AbstractReaderWriter": "./lib/AbstractReaderWriter.js",
- "./DuplexCollection": "./lib/DuplexCollection.js",
- "./fsInterface": "./lib/fsInterface.js",
- "./readers/*": "./lib/readers/*.js",
- "./ReaderCollection": "./lib/ReaderCollection.js",
- "./ReaderCollectionPrioritized": "./lib/ReaderCollectionPrioritized.js",
- "./Resource": "./lib/Resource.js",
- "./resourceFactory": "./lib/resourceFactory.js",
+ "./adapters/*": "./src/adapters/*.ts",
+ "./AbstractReader": "./src/AbstractReader.ts",
+ "./AbstractReaderWriter": "./src/AbstractReaderWriter.ts",
+ "./DuplexCollection": "./src/DuplexCollection.ts",
+ "./fsInterface": "./src/fsInterface.ts",
+ "./readers/*": "./src/readers/*.ts",
+ "./ReaderCollection": "./src/ReaderCollection.ts",
+ "./ReaderCollectionPrioritized": "./src/ReaderCollectionPrioritized.ts",
+ "./Resource": "./src/Resource.ts",
+ "./resourceFactory": "./src/resourceFactory.ts",
"./package.json": "./package.json",
- "./internal/ResourceTagCollection": "./lib/ResourceTagCollection.js"
+ "./internal/ResourceTagCollection": "./src/ResourceTagCollection.ts"
},
"engines": {
"node": "^20.11.0 || >=22.0.0",
"npm": ">= 8"
},
"scripts": {
+ "build": "npm run cleanup && tsc -p tsconfig.build.json",
+ "build-test": "tsc --noEmit -p .",
+ "build-watch": "npm run cleanup && tsc -w -p tsconfig.build.json",
+ "cleanup": "rimraf lib coverage",
"test": "npm run lint && npm run jsdoc-generate && npm run coverage && npm run depcheck",
"test-azure": "npm run coverage-xunit",
"lint": "eslint ./",
@@ -43,17 +47,18 @@
"unit-verbose": "rimraf test/tmp && cross-env UI5_LOG_LVL=verbose ava --verbose --serial",
"unit-watch": "npm run unit -- --watch",
"unit-xunit": "rimraf test/tmp && ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\" --tap | tap-xunit --dontUseCommentsAsTestNames=true > test-results.xml",
- "unit-inspect": "cross-env UI5_LOG_LVL=verbose ava debug --break",
+ "unit-debug": "cross-env UI5_LOG_LVL=verbose ava debug",
+ "unit-update-snapshots": "ava --update-snapshots",
"coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\"",
"coverage-xunit": "nyc --reporter=text --reporter=text-summary --reporter=cobertura npm run unit-xunit",
"jsdoc": "npm run jsdoc-generate && open-cli jsdocs/index.html",
- "jsdoc-generate": "jsdoc -c ./jsdoc.json -t $(node -p 'path.dirname(require.resolve(\"docdash\"))') ./lib/ || (echo 'Error during JSDoc generation! Check log.' && exit 1)",
- "jsdoc-watch": "npm run jsdoc && chokidar \"./lib/**/*.js\" -c \"npm run jsdoc-generate\"",
+ "jsdoc-generate": "typedoc",
+ "jsdoc-watch": "typedoc --watch",
"preversion": "npm test",
"version": "git-chglog --sort semver --next-tag v$npm_package_version -o CHANGELOG.md v4.0.0.. && git add CHANGELOG.md",
"prepublishOnly": "git push --follow-tags",
"release-note": "git-chglog --sort semver -c .chglog/release-config.yml v$npm_package_version",
- "depcheck": "depcheck --ignores @ui5/fs,docdash,@istanbuljs/esm-loader-hook"
+ "depcheck": "depcheck --ignores @ui5/fs,@ui5/project,docdash,@istanbuljs/esm-loader-hook,tsx,typedoc-plugin-rename-defaults"
},
"files": [
"CHANGELOG.md",
@@ -63,21 +68,6 @@
"LICENSES/**",
".reuse/**"
],
- "ava": {
- "files": [
- "test/lib/**/*.js"
- ],
- "watchMode": {
- "ignoreChanges": [
- "test/tmp/**"
- ]
- },
- "nodeArguments": [
- "--loader=esmock",
- "--no-warnings"
- ],
- "workerThreads": false
- },
"nyc": {
"reporter": [
"lcov",
@@ -134,24 +124,31 @@
"random-int": "^3.0.0"
},
"devDependencies": {
- "@eslint/js": "^9.8.0",
+ "@eslint/js": "^9.9.1",
"@istanbuljs/esm-loader-hook": "^0.2.0",
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
+ "@stylistic/eslint-plugin": "^2.6.4",
+ "@types/clone": "^2.1.4",
+ "@types/micromatch": "^4.0.9",
+ "@types/node": "^20.11.0",
+ "@types/pretty-hrtime": "^1.0.3",
+ "@types/sinon": "^17.0.3",
"ava": "^6.1.3",
- "chokidar-cli": "^3.0.0",
"cross-env": "^7.0.3",
"depcheck": "^1.4.7",
"docdash": "^2.0.2",
- "eslint": "^9.9.1",
- "eslint-config-google": "^0.14.0",
+ "eslint": "^9.9.0",
"eslint-plugin-ava": "^15.0.1",
"eslint-plugin-jsdoc": "^50.2.2",
"esmock": "^2.6.7",
- "globals": "^15.9.0",
- "jsdoc": "^4.0.3",
"nyc": "^17.0.0",
"open-cli": "^8.0.0",
"rimraf": "^6.0.1",
"sinon": "^18.0.0",
- "tap-xunit": "^2.4.1"
+ "tap-xunit": "^2.4.1",
+ "tsx": "^4.17.0",
+ "typescript-eslint": "^8.2.0",
+ "typedoc": "^0.26.6",
+ "typedoc-plugin-rename-defaults": "^0.7.1"
}
}
diff --git a/src/AbstractReader.ts b/src/AbstractReader.ts
new file mode 100644
index 00000000..a508283a
--- /dev/null
+++ b/src/AbstractReader.ts
@@ -0,0 +1,121 @@
+import randomInt from "random-int";
+import Trace from "./tracing/Trace.js";
+import {type ResourceInterface} from "./Resource.js";
+
+/**
+ * Abstract resource locator implementing the general API for reading resources
+ */
+class AbstractReader {
+ _name: string | undefined;
+ /**
+ * The constructor.
+ *
+ * @param name Name of the reader. Typically used for tracing purposes
+ */
+ constructor(name?: string) {
+ if (new.target === AbstractReader) {
+ throw new TypeError("Class 'AbstractReader' is abstract");
+ }
+ this._name = name;
+ }
+
+ /*
+ * Returns the name of the reader instance. This can be used for logging/tracing purposes.
+ *
+ * @returns {string} Name of the reader
+ */
+ getName(): string {
+ return this._name ?? ``;
+ }
+
+ /**
+ * Locates resources by matching glob patterns.
+ *
+ * @example
+ * byGlob("**/*.{html,htm}");
+ * byGlob("**/.library");
+ * byGlob("/pony/*");
+ *
+ * @param virPattern glob pattern as string or array of glob patterns for
+ * virtual directory structure
+ * @param [options] glob options
+ * @param [options.nodir] Do not match directories
+ * @returns Promise resolving to list of resources
+ */
+ byGlob(virPattern: string | string[], options = {nodir: true}): Promise {
+ const trace = new Trace(Array.isArray(virPattern) ? virPattern.join("") : virPattern);
+ return this._byGlob(virPattern, options, trace).then(function (result: ResourceInterface[]) {
+ trace.printReport();
+ return result;
+ }).then((resources: ResourceInterface[]) => {
+ if (resources.length > 1) {
+ // Pseudo randomize result order to prevent consumers from relying on it:
+ // Swap the first object with a randomly chosen one
+ const x = 0;
+ const y = randomInt(0, resources.length - 1);
+ // Swap object at index "x" with "y"
+ resources[x] = [resources[y], resources[y] = resources[x]][0];
+ }
+ return resources;
+ });
+ }
+
+ /**
+ * Locates resources by matching a given path.
+ *
+ * @param virPath Virtual path
+ * @param [options] Options
+ * @param [options.nodir] Do not match directories
+ * @returns Promise resolving to a single resource
+ */
+ byPath(virPath: string, options = {nodir: true}) {
+ const trace = new Trace(virPath);
+ return this._byPath(virPath, options, trace).then(function (resource) {
+ trace.printReport();
+ return resource;
+ });
+ }
+
+ /**
+ * Locates resources by one or more glob patterns.
+ *
+ * @param _virPattern glob pattern as string or an array of
+ * glob patterns for virtual directory structure
+ * @param _options glob options
+ * @param _options.nodir Do not match directories
+ * @param _trace Trace instance
+ */
+ _byGlob(_virPattern: string | string[],
+ _options: {
+ nodir: boolean;
+ },
+ _trace: Trace): Promise {
+ throw new Error("Function '_byGlob' is not implemented");
+ }
+
+ /**
+ * Locate resources by matching a single glob pattern.
+ *
+ * @param _pattern glob pattern
+ * @param _options glob options
+ * @param _options.nodir Do not match directories
+ * @param _trace Trace instance
+ */
+ _runGlob(_pattern: string | string[], _options: {nodir: boolean}, _trace: Trace): Promise {
+ throw new Error("Function '_runGlob' is not implemented");
+ }
+
+ /**
+ * Locates resources by path.
+ *
+ * @param _virPath Virtual path
+ * @param _options glob options
+ * @param _options.nodir Do not match directories
+ * @param _trace Trace instance
+ */
+ _byPath(_virPath: string, _options: {nodir: boolean}, _trace: Trace): Promise {
+ throw new Error("Function '_byPath' is not implemented");
+ }
+}
+
+export default AbstractReader;
diff --git a/src/AbstractReaderWriter.ts b/src/AbstractReaderWriter.ts
new file mode 100644
index 00000000..8092b1ec
--- /dev/null
+++ b/src/AbstractReaderWriter.ts
@@ -0,0 +1,71 @@
+import AbstractReader from "./AbstractReader.js";
+import {type ResourceInterface} from "./Resource.js";
+
+/**
+ * Abstract resource locator implementing the general API for reading and writing resources
+ */
+class AbstractReaderWriter extends AbstractReader {
+ /**
+ * The constructor.
+ *
+ * @param name Name of the reader/writer. Typically used for tracing purposes
+ */
+ constructor(name?: string) {
+ if (new.target === AbstractReaderWriter) {
+ throw new TypeError("Class 'AbstractReaderWriter' is abstract");
+ }
+ super(name);
+ }
+
+ /*
+ * Returns the name of the reader/writer instance. This can be used for logging/tracing purposes.
+ *
+ * @returns {string} Name of the reader/writer
+ */
+ getName(): string {
+ return this._name ?? ``;
+ }
+
+ /**
+ * Writes the content of a resource to a path.
+ *
+ * @param resource Resource to write
+ * @param [options] Write options
+ * @param [options.readOnly] Whether the resource content shall be written read-only
+ * Do not use in conjunction with the drain
option.
+ * The written file will be used as the new source of this resources content.
+ * Therefore the written file should not be altered by any means.
+ * Activating this option might improve overall memory consumption.
+ * @param [options.drain] Whether the resource content shall be emptied during the write process.
+ * Do not use in conjunction with the readOnly
option.
+ * Activating this option might improve overall memory consumption.
+ * This should be used in cases where this is the last access to the resource.
+ * E.g. the final write of a resource after all processing is finished.
+ * @returns Promise resolving once data has been written
+ */
+ write(resource: ResourceInterface, options = {drain: false, readOnly: false}): Promise {
+ return this._write(resource, options);
+ }
+
+ /**
+ * Writes the content of a resource to a path.
+ *
+ * @param _resource Resource to write
+ * @param [_options] Write options, see above
+ * @param [_options.drain] Whether the resource content shall be emptied during the write process.
+ * Do not use in conjunction with the readOnly
option.
+ * Activating this option might improve overall memory consumption.
+ * This should be used in cases where this is the last access to the resource.
+ * E.g. the final write of a resource after all processing is finished.
+ * @param [_options.readOnly] Whether the resource content shall be written read-only
+ * Do not use in conjunction with the drain
option.
+ * The written file will be used as the new source of this resources content.
+ * Therefore the written file should not be altered by any means.
+ * Activating this option might improve overall memory consumption.
+ */
+ _write(_resource: ResourceInterface, _options?: {drain?: boolean; readOnly?: boolean}): Promise {
+ throw new Error("Not implemented");
+ }
+}
+
+export default AbstractReaderWriter;
diff --git a/lib/DuplexCollection.js b/src/DuplexCollection.ts
similarity index 50%
rename from lib/DuplexCollection.js
rename to src/DuplexCollection.ts
index 6aec0ec7..c29ff93f 100644
--- a/lib/DuplexCollection.js
+++ b/src/DuplexCollection.ts
@@ -1,25 +1,27 @@
+import type AbstractReader from "./AbstractReader.js";
import AbstractReaderWriter from "./AbstractReaderWriter.js";
import ReaderCollectionPrioritized from "./ReaderCollectionPrioritized.js";
+import {type ResourceInterface} from "./Resource.js";
+import type Trace from "./tracing/Trace.js";
/**
* Wrapper to keep readers and writers together
- *
- * @public
- * @class
- * @alias @ui5/fs/DuplexCollection
- * @extends @ui5/fs/AbstractReaderWriter
*/
class DuplexCollection extends AbstractReaderWriter {
+ _reader: AbstractReader;
+ _writer: AbstractReaderWriter;
+ _combo: ReaderCollectionPrioritized;
+
/**
* The Constructor.
*
- * @param {object} parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers
- * @param {@ui5/fs/AbstractReaderWriter} parameters.writer
+ * @param parameters Parameters
+ * @param parameters.reader Single reader or collection of readers
+ * @param parameters.writer
* A ReaderWriter instance which is only used for writing files
- * @param {string} [parameters.name=""] The collection name
+ * @param [parameters.name] The collection name
*/
- constructor({reader, writer, name = ""}) {
+ constructor({reader, writer, name = ""}: {reader: AbstractReader; writer: AbstractReaderWriter; name?: string}) {
super(name);
if (!reader) {
@@ -36,47 +38,46 @@ class DuplexCollection extends AbstractReaderWriter {
name: `${name} - ReaderCollectionPrioritized`,
readers: [
writer,
- reader
- ]
+ reader,
+ ],
});
}
/**
* Locates resources by glob.
*
- * @private
- * @param {string|string[]} virPattern glob pattern as string or an array of
+ * @param virPattern glob pattern as string or an array of
* glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with a list of resources
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving with a list of resources
*/
- _byGlob(virPattern, options, trace) {
+ _byGlob(virPattern: string | string[], options: {nodir: boolean}, trace: Trace) {
return this._combo._byGlob(virPattern, options, trace);
}
/**
* Locates resources by path.
*
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource|null>}
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns
* Promise resolving to a single resource or null
if no resource is found
*/
- _byPath(virPath, options, trace) {
+ _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
return this._combo._byPath(virPath, options, trace);
}
/**
* Writes the content of a resource to a path.
*
- * @private
- * @param {@ui5/fs/Resource} resource The Resource to write
- * @returns {Promise} Promise resolving once data has been written
+ * @param resource The Resource to write
+ * @returns Promise resolving once data has been written
*/
- _write(resource) {
+ _write(resource: ResourceInterface) {
return this._writer.write(resource);
}
}
diff --git a/src/ReaderCollection.ts b/src/ReaderCollection.ts
new file mode 100644
index 00000000..1354f813
--- /dev/null
+++ b/src/ReaderCollection.ts
@@ -0,0 +1,79 @@
+import AbstractReader from "./AbstractReader.js";
+import {type ResourceInterface} from "./Resource.js";
+import type Trace from "./tracing/Trace.js";
+
+/**
+ * Resource Locator ReaderCollection
+ */
+class ReaderCollection extends AbstractReader {
+ _readers: AbstractReader[];
+ /**
+ * The constructor.
+ *
+ * @param parameters Parameters
+ * @param [parameters.name] The collection name
+ * @param [parameters.readers]
+ * List of resource readers (all tried in parallel).
+ * If none are provided, the collection will never return any results.
+ */
+ constructor({name, readers}: {name?: string; readers?: AbstractReader[]}) {
+ super(name);
+
+ // Remove any undefined (empty) readers from array
+ this._readers = readers?.filter(($) => $) ?? [];
+ }
+
+ /**
+ * Locates resources by glob.
+ *
+ * @param pattern glob pattern as string or an array of
+ * glob patterns for virtual directory structure
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
+ */
+ _byGlob(pattern: string | string[], options: {nodir: boolean}, trace: Trace) {
+ return Promise.all(this._readers.map(function (resourceLocator) {
+ return resourceLocator._byGlob(pattern, options, trace);
+ })).then((result) => {
+ trace.collection(this._name!);
+ return Array.prototype.concat.apply([], result) as ResourceInterface[]; // Flatten array
+ });
+ }
+
+ /**
+ * Locates resources by path.
+ *
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns
+ * Promise resolving to a single resource or null
if no resource is found
+ */
+ _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
+ const resourceLocatorCount = this._readers.length;
+
+ if (resourceLocatorCount === 0) {
+ // Short-circuit if there are no readers (Promise.race does not settle for empty arrays)
+ trace.collection(this._name!);
+ return Promise.resolve(null);
+ }
+
+ // Using Promise.race to deliver files that can be found as fast as possible
+ return Promise.race(this._readers.map((resourceLocator) => {
+ return resourceLocator._byPath(virPath, options, trace).then((resource) => {
+ trace.collection(this._name!);
+ if (resource) {
+ resource.pushCollection(this._name!);
+ return resource;
+ } else {
+ return null;
+ }
+ });
+ }));
+ }
+}
+
+export default ReaderCollection;
diff --git a/src/ReaderCollectionPrioritized.ts b/src/ReaderCollectionPrioritized.ts
new file mode 100644
index 00000000..4ad4aba6
--- /dev/null
+++ b/src/ReaderCollectionPrioritized.ts
@@ -0,0 +1,93 @@
+import AbstractReader from "./AbstractReader.js";
+import {type ResourceInterface} from "./Resource.js";
+import type Trace from "./tracing/Trace.js";
+
+/**
+ * Prioritized Resource Locator Collection
+ */
+class ReaderCollectionPrioritized extends AbstractReader {
+ _readers: AbstractReader[];
+
+ /**
+ * The constructor.
+ *
+ * @param parameters Parameters
+ * @param parameters.name The collection name
+ * @param [parameters.readers]
+ * Prioritized list of resource readers (tried in the order provided).
+ * If none are provided, the collection will never return any results.
+ */
+ constructor({readers, name}: {readers?: AbstractReader[]; name: string}) {
+ super(name);
+
+ // Remove any undefined (empty) readers from array
+ this._readers = readers?.filter(($) => $) ?? [];
+ }
+
+ /**
+ * Locates resources by glob.
+ *
+ * @param pattern glob pattern as string or an array of
+ * glob patterns for virtual directory structure
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
+ */
+ _byGlob(pattern: string | string[], options: {nodir: boolean}, trace: Trace) {
+ return Promise.all(this._readers.map(function (resourceLocator) {
+ return resourceLocator._byGlob(pattern, options, trace);
+ })).then((result) => {
+ const files = Object.create(null) as Record;
+ const resources = [];
+ // Prefer files found in preceding resource locators
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
+ for (let i = 0; i < result.length; i++) {
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
+ for (let j = 0; j < result[i].length; j++) {
+ const resource = result[i][j];
+ const path = resource.getPath();
+ if (!files[path]) {
+ files[path] = true;
+ resources.push(resource);
+ }
+ }
+ }
+
+ trace.collection(this._name!);
+ return resources;
+ });
+ }
+
+ /**
+ * Locates resources by path.
+ *
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns
+ * Promise resolving to a single resource or null
if no resource is found
+ */
+ _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
+ // const that = this;
+ const byPath = (i: number) => {
+ if (i > this._readers.length - 1) {
+ return Promise.resolve(null);
+ }
+ return this._readers[i]._byPath(virPath, options, trace)
+ .then((resource: ResourceInterface | null): ResourceInterface | Promise => {
+ if (resource) {
+ resource.pushCollection(this._name!);
+ return resource;
+ } else {
+ return byPath(++i);
+ }
+ });
+ };
+
+ return byPath(0);
+ }
+}
+
+export default ReaderCollectionPrioritized;
diff --git a/lib/Resource.js b/src/Resource.ts
similarity index 63%
rename from lib/Resource.js
rename to src/Resource.ts
index 0eb10b39..3b61aa93 100644
--- a/lib/Resource.js
+++ b/src/Resource.ts
@@ -1,69 +1,133 @@
import stream from "node:stream";
import clone from "clone";
import posixPath from "node:path/posix";
+import {Buffer} from "node:buffer";
+import type {Stats} from "node:fs";
+import type {Project} from "@ui5/project/specifications/Project";
+import {isString} from "./utils/tsUtils.js";
const fnTrue = () => true;
const fnFalse = () => false;
-const ALLOWED_SOURCE_METADATA_KEYS = ["adapter", "fsPath", "contentModified"];
+
+enum ALLOWED_SOURCE_METADATA_KEYS {
+ ADAPTER = "adapter",
+ FS_PATH = "fsPath",
+ CONTENT_MODIFIED = "contentModified",
+};
/**
- * Resource. UI5 Tooling specific representation of a file's content and metadata
+ * Function for dynamic creation of content streams
*
- * @public
- * @class
- * @alias @ui5/fs/Resource
+ * @returns {stream.Readable} A readable stream of a resources content
*/
-class Resource {
- #project;
- #buffer;
- #buffering;
- #collections;
- #contentDrained;
- #createStream;
- #name;
- #path;
- #sourceMetadata;
- #statInfo;
- #stream;
- #streamDrained;
- #isModified;
+export type Resource_CreateReadableStream = () => stream.Readable;
+
+export interface Resource_sourceMetadata {
+ [ALLOWED_SOURCE_METADATA_KEYS.ADAPTER]?: string;
+ [ALLOWED_SOURCE_METADATA_KEYS.FS_PATH]?: string;
+ [ALLOWED_SOURCE_METADATA_KEYS.CONTENT_MODIFIED]?: boolean;
+};
+
+// TODO: Validate these options.
+// Some might be required while others can be optional.
+// Different combinations can be ok.
+export interface Resource_Options {
+ path: string;
+ // It could be a real Stats, but also a Stats like object
+ statInfo?: Partial;
+ buffer?: Buffer;
+ string?: string;
+ createStream?: Resource_CreateReadableStream;
+ stream?: stream.Readable;
+ project?: Project;
+ sourceMetadata?: Resource_sourceMetadata;
+ source?: {
+ adapter?: "Abstract";
+ };
+};
+
+export interface Tree {[x: string]: object | Tree};
+
+export interface LegacyResource {
+ _path: string;
+ // It could be a real Stats, but also a Stats like object
+ _statInfo?: Partial;
+ _source?: {
+ adapter?: "Abstract";
+ };
+ _createStream?: Resource_CreateReadableStream;
+ _stream?: stream.Readable;
+ _buffer?: Buffer;
+ _getBufferFromStream?: () => Promise;
+}
- /**
- * Function for dynamic creation of content streams
- *
- * @public
- * @callback @ui5/fs/Resource~createStream
- * @returns {stream.Readable} A readable stream of a resources content
- */
+export interface ResourceInterface {
+ clone(): Promise;
+ getBuffer(): Promise;
+ getName(): string;
+ getPath(): string;
+ getPathTree(): Tree;
+ getProject(): Project | undefined;
+ getSourceMetadata(): Resource_sourceMetadata;
+ getSize(): Promise;
+ getStatInfo(): Partial;
+ getStream(): stream.Readable;
+ getString(): Promise;
+ hasProject(): boolean;
+ isModified(): boolean;
+ pushCollection(name: string): void;
+ setBuffer(buffer: Buffer): void;
+ setPath(path: string): void;
+ setProject(project: Project): void;
+ setStream(stream: stream.Readable | Resource_CreateReadableStream): void;
+ setString(string: string): void;
+}
+/**
+ * Resource. UI5 Tooling specific representation of a file's content and metadata
+ */
+class Resource implements ResourceInterface {
+ #project;
+ #buffer: Buffer | null | undefined;
+ #buffering: Promise | null | undefined;
+ #collections: string[];
+ #contentDrained: boolean | undefined;
+ #createStream: Resource_CreateReadableStream | null | undefined;
+ #name!: string;
+ #path!: string;
+ #sourceMetadata: Resource_sourceMetadata;
+ #statInfo: Partial;
+ #stream: stream.Readable | null | undefined;
+ #streamDrained: boolean | undefined;
+ #isModified: boolean;
/**
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.path Absolute virtual path of the resource
- * @param {fs.Stats|object} [parameters.statInfo] File information. Instance of
+ * @param parameters Parameters
+ * @param parameters.path Absolute virtual path of the resource
+ * @param [parameters.statInfo] File information. Instance of
* [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} or similar object
- * @param {Buffer} [parameters.buffer] Content of this resources as a Buffer instance
+ * @param [parameters.buffer] Content of this resources as a Buffer instance
* (cannot be used in conjunction with parameters string, stream or createStream)
- * @param {string} [parameters.string] Content of this resources as a string
+ * @param [parameters.string] Content of this resources as a string
* (cannot be used in conjunction with parameters buffer, stream or createStream)
- * @param {Stream} [parameters.stream] Readable stream of the content of this resource
+ * @param [parameters.stream] Readable stream of the content of this resource
* (cannot be used in conjunction with parameters buffer, string or createStream)
- * @param {@ui5/fs/Resource~createStream} [parameters.createStream] Function callback that returns a readable
+ * @param [parameters.createStream] Function callback that returns a readable
* stream of the content of this resource (cannot be used in conjunction with parameters buffer,
* string or stream).
* In some cases this is the most memory-efficient way to supply resource content
- * @param {@ui5/project/specifications/Project} [parameters.project] Project this resource is associated with
- * @param {object} [parameters.sourceMetadata] Source metadata for UI5 Tooling internal use.
+ * @param [parameters.project] Project this resource is associated with
+ * @param [parameters.sourceMetadata] Source metadata for UI5 Tooling internal use.
* Some information may be set by an adapter to store information for later retrieval. Also keeps track of whether
* a resource content has been modified since it has been read from a source
*/
- constructor({path, statInfo, buffer, string, createStream, stream, project, sourceMetadata}) {
+ constructor({path, statInfo, buffer, string, createStream, stream, project, sourceMetadata}: Resource_Options
+ ) {
if (!path) {
throw new Error("Unable to create Resource: Missing parameter 'path'");
}
- if (buffer && createStream || buffer && string || string && createStream || buffer && stream ||
- string && stream || createStream && stream) {
+ if ((buffer && createStream) || (buffer && string) || (string && createStream) || (buffer && stream) ||
+ (string && stream) || (createStream && stream)) {
throw new Error("Unable to create Resource: Please set only one content parameter. " +
"'buffer', 'string', 'stream' or 'createStream'");
}
@@ -73,12 +137,16 @@ class Resource {
throw new Error(`Parameter 'sourceMetadata' must be of type "object"`);
}
- /* eslint-disable-next-line guard-for-in */
+ // TODO: TS Those checks are completely redundant, but some tests
+ // and maybe runtime code would rely on them. A major refactoring
+ // would be needed
for (const metadataKey in sourceMetadata) { // Also check prototype
- if (!ALLOWED_SOURCE_METADATA_KEYS.includes(metadataKey)) {
+ if (!Object.values(ALLOWED_SOURCE_METADATA_KEYS)
+ .includes(metadataKey as ALLOWED_SOURCE_METADATA_KEYS)) {
throw new Error(`Parameter 'sourceMetadata' contains an illegal attribute: ${metadataKey}`);
}
- if (!["string", "boolean"].includes(typeof sourceMetadata[metadataKey])) {
+ if (!["string", "boolean"]
+ .includes(typeof sourceMetadata[metadataKey as ALLOWED_SOURCE_METADATA_KEYS])) {
throw new Error(
`Attribute '${metadataKey}' of parameter 'sourceMetadata' ` +
`must be of type "string" or "boolean"`);
@@ -88,7 +156,7 @@ class Resource {
this.setPath(path);
- this.#sourceMetadata = sourceMetadata || {};
+ this.#sourceMetadata = sourceMetadata ?? {};
// This flag indicates whether a resource has changed from its original source.
// resource.isModified() is not sufficient, since it only reflects the modification state of the
@@ -100,7 +168,7 @@ class Resource {
this.#project = project;
- this.#statInfo = statInfo || { // TODO
+ this.#statInfo = statInfo ?? { // TODO
isFile: fnTrue,
isDirectory: fnFalse,
isBlockDevice: fnFalse,
@@ -115,7 +183,7 @@ class Resource {
atime: new Date(),
mtime: new Date(),
ctime: new Date(),
- birthtime: new Date()
+ birthtime: new Date(),
};
if (createStream) {
@@ -125,7 +193,7 @@ class Resource {
} else if (buffer) {
// Use private setter, not to accidentally set any modified flags
this.#setBuffer(buffer);
- } else if (typeof string === "string" || string instanceof String) {
+ } else if (isString(string)) {
// Use private setter, not to accidentally set any modified flags
this.#setBuffer(Buffer.from(string, "utf8"));
}
@@ -137,10 +205,9 @@ class Resource {
/**
* Gets a buffer with the resource content.
*
- * @public
- * @returns {Promise} Promise resolving with a buffer of the resource content.
+ * @returns Promise resolving with a buffer of the resource content.
*/
- async getBuffer() {
+ async getBuffer(): Promise {
if (this.#contentDrained) {
throw new Error(`Content of Resource ${this.#path} has been drained. ` +
"This might be caused by requesting resource content after a content stream has been " +
@@ -158,16 +225,15 @@ class Resource {
/**
* Sets a Buffer as content.
*
- * @public
- * @param {Buffer} buffer Buffer instance
+ * @param buffer Buffer instance
*/
- setBuffer(buffer) {
+ setBuffer(buffer: Buffer) {
this.#sourceMetadata.contentModified = true;
this.#isModified = true;
this.#setBuffer(buffer);
}
- #setBuffer(buffer) {
+ #setBuffer(buffer: Buffer) {
this.#createStream = null;
// if (this.#stream) { // TODO this may cause strange issues
// this.#stream.destroy();
@@ -181,10 +247,9 @@ class Resource {
/**
* Gets a string with the resource content.
*
- * @public
- * @returns {Promise} Promise resolving with the resource content.
+ * @returns Promise resolving with the resource content.
*/
- getString() {
+ getString(): Promise {
if (this.#contentDrained) {
return Promise.reject(new Error(`Content of Resource ${this.#path} has been drained. ` +
"This might be caused by requesting resource content after a content stream has been " +
@@ -196,10 +261,9 @@ class Resource {
/**
* Sets a String as content
*
- * @public
- * @param {string} string Resource content
+ * @param string Resource content
*/
- setString(string) {
+ setString(string: string) {
this.setBuffer(Buffer.from(string, "utf8"));
}
@@ -211,10 +275,9 @@ class Resource {
* or [setString]{@link @ui5/fs/Resource#setString}). This
* is to prevent consumers from accessing drained streams.
*
- * @public
- * @returns {stream.Readable} Readable stream for the resource content.
+ * @returns Readable stream for the resource content.
*/
- getStream() {
+ getStream(): stream.Readable {
if (this.#contentDrained) {
throw new Error(`Content of Resource ${this.#path} has been drained. ` +
"This might be caused by requesting resource content after a content stream has been " +
@@ -246,11 +309,10 @@ class Resource {
/**
* Sets a readable stream as content.
*
- * @public
- * @param {stream.Readable|@ui5/fs/Resource~createStream} stream Readable stream of the resource content or
+ * @param stream Readable stream of the resource content or
callback for dynamic creation of a readable stream
*/
- setStream(stream) {
+ setStream(stream: stream.Readable | Resource_CreateReadableStream) {
this.#isModified = true;
this.#sourceMetadata.contentModified = true;
@@ -272,20 +334,18 @@ class Resource {
/**
* Gets the virtual resources path
*
- * @public
- * @returns {string} Virtual path of the resource
+ * @returns Virtual path of the resource
*/
- getPath() {
- return this.#path;
+ getPath(): string {
+ return this.#path ?? "";
}
/**
* Sets the virtual resources path
*
- * @public
- * @param {string} path Absolute virtual path of the resource
+ * @param path Absolute virtual path of the resource
*/
- setPath(path) {
+ setPath(path: string) {
path = posixPath.normalize(path);
if (!posixPath.isAbsolute(path)) {
throw new Error(`Unable to set resource path: Path must be absolute: ${path}`);
@@ -297,10 +357,9 @@ class Resource {
/**
* Gets the resource name
*
- * @public
- * @returns {string} Name of the resource
+ * @returns Name of the resource
*/
- getName() {
+ getName(): string {
return this.#name;
}
@@ -310,11 +369,10 @@ class Resource {
* Also, depending on the used adapter, some fields might be missing which would be present for a
* [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} instance.
*
- * @public
- * @returns {fs.Stats|object} Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats}
+ * @returns Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats}
* or similar object
*/
- getStatInfo() {
+ getStatInfo(): Partial {
return this.#statInfo;
}
@@ -322,9 +380,9 @@ class Resource {
* Size in bytes allocated by the underlying buffer.
*
* @see {TypedArray#byteLength}
- * @returns {Promise} size in bytes, 0
if there is no content yet
+ * @returns size in bytes, 0
if there is no content yet
*/
- async getSize() {
+ async getSize(): Promise {
// if resource does not have any content it should have 0 bytes
if (!this.#buffer && !this.#createStream && !this.#stream) {
return 0;
@@ -336,28 +394,27 @@ class Resource {
/**
* Adds a resource collection name that was involved in locating this resource.
*
- * @param {string} name Resource collection name
+ * @param name Resource collection name
*/
- pushCollection(name) {
+ pushCollection(name: string) {
this.#collections.push(name);
}
/**
* Returns a clone of the resource. The clones content is independent from that of the original resource
*
- * @public
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving with the clone
+ * @returns Promise resolving with the clone
*/
- async clone() {
+ async clone(): Promise {
const options = await this.#getCloneOptions();
return new Resource(options);
}
- async #getCloneOptions() {
- const options = {
+ async #getCloneOptions(): Promise {
+ const options: Resource_Options = {
path: this.#path,
statInfo: clone(this.#statInfo),
- sourceMetadata: clone(this.#sourceMetadata)
+ sourceMetadata: clone(this.#sourceMetadata),
};
if (this.#stream) {
@@ -381,23 +438,21 @@ class Resource {
* [MiddlewareUtil]{@link module:@ui5/server.middleware.MiddlewareUtil}, which will
* return a Specification Version-compatible Project interface.
*
- * @public
- * @returns {@ui5/project/specifications/Project} Project this resource is associated with
+ * @returns Project this resource is associated with
*/
- getProject() {
+ getProject(): Project | undefined {
return this.#project;
}
/**
* Assign a project to the resource
*
- * @public
- * @param {@ui5/project/specifications/Project} project Project this resource is associated with
+ * @param project Project this resource is associated with
*/
- setProject(project) {
+ setProject(project: 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}`);
+ `Resource is already associated to project ${this.#project.getName()}`);
}
this.#project = project;
}
@@ -405,35 +460,33 @@ class Resource {
/**
* Check whether a project has been assigned to the resource
*
- * @public
- * @returns {boolean} True if the resource is associated with a project
+ * @returns True if the resource is associated with a project
*/
- hasProject() {
+ hasProject(): boolean {
return !!this.#project;
}
/**
* Check whether the content of this resource has been changed during its life cycle
*
- * @public
- * @returns {boolean} True if the resource's content has been changed
+ * @returns True if the resource's content has been changed
*/
- isModified() {
+ isModified(): boolean {
return this.#isModified;
}
/**
* Tracing: Get tree for printing out trace
*
- * @returns {object} Trace tree
+ * @returns Trace tree
*/
- getPathTree() {
- const tree = Object.create(null);
+ getPathTree(): Tree {
+ const tree = Object.create(null) as Tree;
- let pointer = tree[this.#path] = Object.create(null);
+ let pointer = tree[this.#path] = Object.create(null) as Tree;
for (let i = this.#collections.length - 1; i >= 0; i--) {
- pointer = pointer[this.#collections[i]] = Object.create(null);
+ pointer = pointer[this.#collections[i]] = Object.create(null) as Tree;
}
return tree;
@@ -443,19 +496,18 @@ class Resource {
* Returns source metadata which may contain information specific to the adapter that created the resource
* Typically set by an adapter to store information for later retrieval.
*
- * @returns {object}
+ * @returns
*/
- getSourceMetadata() {
+ getSourceMetadata(): Resource_sourceMetadata {
return this.#sourceMetadata;
}
/**
* Returns the content as stream.
*
- * @private
- * @returns {stream.Readable} Readable stream
+ * @returns Readable stream
*/
- #getStream() {
+ #getStream(): stream.Readable {
if (this.#streamDrained) {
throw new Error(`Content stream of Resource ${this.#path} is flagged as drained.`);
}
@@ -463,23 +515,23 @@ class Resource {
return this.#createStream();
}
this.#streamDrained = true;
- return this.#stream;
+ return this.#stream!;
}
/**
* Converts the buffer into a stream.
*
- * @private
- * @returns {Promise} Promise resolving with buffer.
+ * @returns Promise resolving with buffer.
*/
- #getBufferFromStream() {
+ #getBufferFromStream(): Promise {
if (this.#buffering) { // Prevent simultaneous buffering, causing unexpected access to drained stream
return this.#buffering;
}
return this.#buffering = new Promise((resolve, reject) => {
const contentStream = this.#getStream();
- const buffers = [];
- contentStream.on("data", (data) => {
+ const buffers: Buffer[] = [];
+
+ contentStream.on("data", (data: Buffer) => {
buffers.push(data);
});
contentStream.on("error", (err) => {
diff --git a/lib/ResourceFacade.js b/src/ResourceFacade.ts
similarity index 70%
rename from lib/ResourceFacade.js
rename to src/ResourceFacade.ts
index ef6dcdf8..7a864e65 100644
--- a/lib/ResourceFacade.js
+++ b/src/ResourceFacade.ts
@@ -1,25 +1,24 @@
import posixPath from "node:path/posix";
+import {type Buffer} from "node:buffer";
+import type stream from "node:stream";
+import {type Resource_CreateReadableStream, type ResourceInterface} from "./Resource.js";
+import {type Project} from "@ui5/project/specifications/Project";
/**
* A {@link @ui5/fs/Resource Resource} with a different path than it's original
- *
- * @public
- * @class
- * @alias @ui5/fs/ResourceFacade
*/
-class ResourceFacade {
+class ResourceFacade implements ResourceInterface {
#path;
#name;
#resource;
/**
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.path Virtual path of the facade resource
- * @param {@ui5/fs/Resource} parameters.resource Resource to conceal
+ * @param parameters Parameters
+ * @param parameters.path Virtual path of the facade resource
+ * @param parameters.resource Resource to conceal
*/
- constructor({path, resource}) {
+ constructor({path, resource}: {path: string; resource: ResourceInterface}) {
if (!path) {
throw new Error("Unable to create ResourceFacade: Missing parameter 'path'");
}
@@ -38,8 +37,7 @@ class ResourceFacade {
/**
* Gets the resources path
*
- * @public
- * @returns {string} (Virtual) path of the resource
+ * @returns (Virtual) path of the resource
*/
getPath() {
return this.#path;
@@ -48,8 +46,7 @@ class ResourceFacade {
/**
* Gets the resource name
*
- * @public
- * @returns {string} Name of the resource
+ * @returns Name of the resource
*/
getName() {
return this.#name;
@@ -58,10 +55,9 @@ class ResourceFacade {
/**
* Sets the resources path
*
- * @public
- * @param {string} path (Virtual) path of the resource
+ * @param _path (Virtual) path of the resource
*/
- setPath(path) {
+ setPath(_path: string) {
throw new Error(`The path of a ResourceFacade can't be changed`);
}
@@ -69,8 +65,7 @@ class ResourceFacade {
* Returns a clone of the resource. The clones content is independent from that of the original resource.
* A ResourceFacade becomes a Resource
*
- * @public
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving with the clone
+ * @returns Promise resolving with the clone
*/
async clone() {
// Cloning resolves the facade
@@ -87,8 +82,7 @@ class ResourceFacade {
/**
* Gets a buffer with the resource content.
*
- * @public
- * @returns {Promise} Promise resolving with a buffer of the resource content.
+ * @returns Promise resolving with a buffer of the resource content.
*/
async getBuffer() {
return this.#resource.getBuffer();
@@ -97,18 +91,16 @@ class ResourceFacade {
/**
* Sets a Buffer as content.
*
- * @public
- * @param {Buffer} buffer Buffer instance
+ * @param buffer Buffer instance
*/
- setBuffer(buffer) {
+ setBuffer(buffer: Buffer) {
return this.#resource.setBuffer(buffer);
}
/**
* Gets a string with the resource content.
*
- * @public
- * @returns {Promise} Promise resolving with the resource content.
+ * @returns Promise resolving with the resource content.
*/
getString() {
return this.#resource.getString();
@@ -117,10 +109,9 @@ class ResourceFacade {
/**
* Sets a String as content
*
- * @public
- * @param {string} string Resource content
+ * @param string Resource content
*/
- setString(string) {
+ setString(string: string) {
return this.#resource.setString(string);
}
@@ -132,8 +123,7 @@ class ResourceFacade {
* or [setString]{@link @ui5/fs/Resource#setString}). This
* is to prevent consumers from accessing drained streams.
*
- * @public
- * @returns {stream.Readable} Readable stream for the resource content.
+ * @returns Readable stream for the resource content.
*/
getStream() {
return this.#resource.getStream();
@@ -142,11 +132,10 @@ class ResourceFacade {
/**
* Sets a readable stream as content.
*
- * @public
- * @param {stream.Readable|@ui5/fs/Resource~createStream} stream Readable stream of the resource content or
+ * @param stream Readable stream of the resource content or
callback for dynamic creation of a readable stream
*/
- setStream(stream) {
+ setStream(stream: stream.Readable | Resource_CreateReadableStream) {
return this.#resource.setStream(stream);
}
@@ -156,8 +145,7 @@ class ResourceFacade {
* Also, depending on the used adapter, some fields might be missing which would be present for a
* [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} instance.
*
- * @public
- * @returns {fs.Stats|object} Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats}
+ * @returns Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats}
* or similar object
*/
getStatInfo() {
@@ -168,25 +156,25 @@ class ResourceFacade {
* Size in bytes allocated by the underlying buffer.
*
* @see {TypedArray#byteLength}
- * @returns {Promise} size in bytes, 0
if there is no content yet
+ * @returns size in bytes, 0
if there is no content yet
*/
- async getSize() {
+ getSize() {
return this.#resource.getSize();
}
/**
* Adds a resource collection name that was involved in locating this resource.
*
- * @param {string} name Resource collection name
+ * @param name Resource collection name
*/
- pushCollection(name) {
+ pushCollection(name: string) {
return this.#resource.pushCollection(name);
}
/**
* Tracing: Get tree for printing out trace
*
- * @returns {object} Trace tree
+ * @returns Trace tree
*/
getPathTree() {
return this.#resource.getPathTree();
@@ -202,8 +190,7 @@ class ResourceFacade {
* [MiddlewareUtil]{@link module:@ui5/server.middleware.MiddlewareUtil}, which will
* return a Specification Version-compatible Project interface.
*
- * @public
- * @returns {@ui5/project/specifications/Project} Project this resource is associated with
+ * @returns Project this resource is associated with
*/
getProject() {
return this.#resource.getProject();
@@ -212,27 +199,25 @@ class ResourceFacade {
/**
* Assign a project to the resource
*
- * @public
- * @param {@ui5/project/specifications/Project} project Project this resource is associated with
+ * @param project Project this resource is associated with
*/
- setProject(project) {
+ setProject(project: Project) {
return this.#resource.setProject(project);
}
/**
* Check whether a project has been assigned to the resource
*
- * @public
- * @returns {boolean} True if the resource is associated with a project
+ * @returns True if the resource is associated with a project
*/
hasProject() {
return this.#resource.hasProject();
}
+
/**
* Check whether the content of this resource has been changed during its life cycle
*
- * @public
- * @returns {boolean} True if the resource's content has been changed
+ * @returns True if the resource's content has been changed
*/
isModified() {
return this.#resource.isModified();
@@ -242,17 +227,16 @@ class ResourceFacade {
* Returns source metadata if any where provided during the creation of this resource.
* Typically set by an adapter to store information for later retrieval.
*
- * @returns {object|null}
+ * @returns
*/
getSourceMetadata() {
return this.#resource.getSourceMetadata();
}
-
/**
* Returns the resource concealed by this facade
*
- * @returns {@ui5/fs/Resource}
+ * @returns
*/
getConcealedResource() {
return this.#resource;
diff --git a/lib/ResourceTagCollection.js b/src/ResourceTagCollection.ts
similarity index 65%
rename from lib/ResourceTagCollection.js
rename to src/ResourceTagCollection.ts
index 9214c15b..992bc3a9 100644
--- a/lib/ResourceTagCollection.js
+++ b/src/ResourceTagCollection.ts
@@ -1,16 +1,30 @@
const tagNamespaceRegExp = /^[a-z][a-z0-9]+$/; // part before the colon
const tagNameRegExp = /^[A-Z][A-Za-z0-9]+$/; // part after the colon
+import {type ResourceInterface} from "./Resource.js";
import ResourceFacade from "./ResourceFacade.js";
+interface PathTagsInterface {
+ [key: string]: string | number | boolean | undefined | PathTagsInterface;
+};
/**
- * A ResourceTagCollection
*
- * @private
- * @class
- * @alias @ui5/fs/internal/ResourceTagCollection
+ * @param elem Variable to test if it's with a PathTagsInterface type
+ */
+export function isPathTagsInterface(elem: unknown): elem is PathTagsInterface {
+ return typeof elem === "object";
+}
+
+/**
+ * A ResourceTagCollection
*/
class ResourceTagCollection {
- constructor({allowedTags = [], allowedNamespaces = [], tags}) {
+ _allowedTags: string[];
+ _allowedNamespaces: string[];
+ _pathTags: PathTagsInterface;
+ _allowedNamespacesRegExp: null | RegExp;
+
+ constructor({allowedTags = [], allowedNamespaces = [], tags}:
+ {allowedTags?: string[]; allowedNamespaces?: string[]; tags?: PathTagsInterface}) {
this._allowedTags = allowedTags; // Allowed tags are validated during use
this._allowedNamespaces = allowedNamespaces;
@@ -29,35 +43,42 @@ class ResourceTagCollection {
this._allowedNamespacesRegExp = null;
}
- this._pathTags = tags || Object.create(null);
+ this._pathTags = tags ?? Object.create(null) as PathTagsInterface;
}
- setTag(resourcePath, tag, value = true) {
+ setTag(resourcePath: ResourceInterface | string, tag: string, value: string | number | boolean = true) {
resourcePath = this._getPath(resourcePath);
this._validateTag(tag);
this._validateValue(value);
if (!this._pathTags[resourcePath]) {
- this._pathTags[resourcePath] = Object.create(null);
+ this._pathTags[resourcePath] = Object.create(null) as PathTagsInterface;
+ }
+
+ const pointer = this._pathTags[resourcePath];
+ if (isPathTagsInterface(pointer)) {
+ pointer[tag] = value;
}
- this._pathTags[resourcePath][tag] = value;
}
- clearTag(resourcePath, tag) {
+ clearTag(resourcePath: ResourceInterface | string, tag: string) {
resourcePath = this._getPath(resourcePath);
this._validateTag(tag);
- if (this._pathTags[resourcePath]) {
- this._pathTags[resourcePath][tag] = undefined;
+ const pointer = this._pathTags[resourcePath];
+ if (isPathTagsInterface(pointer)) {
+ pointer[tag] = undefined;
}
}
- getTag(resourcePath, tag) {
+ getTag(resourcePath: ResourceInterface | string,
+ tag: string): string | number | boolean | undefined | PathTagsInterface {
resourcePath = this._getPath(resourcePath);
this._validateTag(tag);
- if (this._pathTags[resourcePath]) {
- return this._pathTags[resourcePath][tag];
+ const pointer = this._pathTags[resourcePath];
+ if (isPathTagsInterface(pointer)) {
+ return pointer[tag];
}
}
@@ -65,14 +86,14 @@ class ResourceTagCollection {
return this._pathTags;
}
- acceptsTag(tag) {
+ acceptsTag(tag: string) {
if (this._allowedTags.includes(tag) || this._allowedNamespacesRegExp?.test(tag)) {
return true;
}
return false;
}
- _getPath(resourcePath) {
+ _getPath(resourcePath: ResourceInterface | string): string {
if (typeof resourcePath !== "string") {
if (resourcePath instanceof ResourceFacade) {
resourcePath = resourcePath.getConcealedResource().getPath();
@@ -86,7 +107,7 @@ class ResourceTagCollection {
return resourcePath;
}
- _validateTag(tag) {
+ _validateTag(tag: string) {
if (!tag.includes(":")) {
throw new Error(`Invalid Tag "${tag}": Colon required after namespace`);
}
@@ -112,7 +133,7 @@ class ResourceTagCollection {
}
}
- _validateValue(value) {
+ _validateValue(value: string | number | boolean) {
const type = typeof value;
if (!["string", "number", "boolean"].includes(type)) {
throw new Error(
diff --git a/lib/WriterCollection.js b/src/WriterCollection.ts
similarity index 56%
rename from lib/WriterCollection.js
rename to src/WriterCollection.ts
index 7c01f783..e6b62369 100644
--- a/lib/WriterCollection.js
+++ b/src/WriterCollection.ts
@@ -1,22 +1,23 @@
import AbstractReaderWriter from "./AbstractReaderWriter.js";
import ReaderCollection from "./ReaderCollection.js";
import escapeStringRegExp from "escape-string-regexp";
+import type Trace from "./tracing/Trace.js";
+import {type ResourceInterface} from "./Resource.js";
/**
* Resource Locator WriterCollection
- *
- * @public
- * @class
- * @alias @ui5/fs/WriterCollection
- * @extends @ui5/fs/AbstractReaderWriter
*/
class WriterCollection extends AbstractReaderWriter {
+ _basePathRegex: string;
+ _writerMapping: Record;
+ _readerCollection: ReaderCollection;
+
/**
* The constructor.
*
- * @param {object} parameters Parameters
- * @param {string} parameters.name The collection name
- * @param {object.} parameters.writerMapping
+ * @param parameters Parameters
+ * @param parameters.name The collection name
+ * @param parameters.writerMapping
* Mapping of virtual base paths to writers. Path are matched greedy
*
* @example
@@ -28,7 +29,7 @@ class WriterCollection extends AbstractReaderWriter {
* }
* });
*/
- constructor({name, writerMapping}) {
+ constructor({name, writerMapping}: {name: string; writerMapping: Record}) {
super(name);
if (!writerMapping) {
@@ -41,7 +42,7 @@ class WriterCollection extends AbstractReaderWriter {
// Create a regular expression (which is greedy by nature) from all paths to easily
// find the correct writer for any given resource path
- this._basePathRegex = basePaths.sort().reduce((regex, basePath, idx) => {
+ this._basePathRegex = basePaths.sort().reduce((regex, basePath) => {
// Validate base path
if (!basePath) {
throw new Error(`Empty path in path mapping of WriterCollection ${this._name}`);
@@ -61,46 +62,55 @@ class WriterCollection extends AbstractReaderWriter {
this._writerMapping = writerMapping;
this._readerCollection = new ReaderCollection({
name: `Reader collection of writer collection '${this._name}'`,
- readers: Object.values(writerMapping)
+ readers: Object.values(writerMapping),
});
}
/**
* Locates resources by glob.
*
- * @private
- * @param {string|string[]} pattern glob pattern as string or an array of
+ * @param pattern glob pattern as string or an array of
* glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
*/
- _byGlob(pattern, options, trace) {
+ _byGlob(pattern: string | string[], options: {nodir: boolean}, trace: Trace) {
return this._readerCollection._byGlob(pattern, options, trace);
}
/**
* Locates resources by path.
*
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to a single resource
*/
- _byPath(virPath, options, trace) {
+ _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
return this._readerCollection._byPath(virPath, options, trace);
}
/**
* Writes the content of a resource to a path.
*
- * @private
- * @param {@ui5/fs/Resource} resource The Resource to write
- * @param {object} [options] Write options, see above
- * @returns {Promise} Promise resolving once data has been written
+ * @param resource The Resource to write
+ * @param [options] Write options, see above
+ * @param [options.drain] Whether the resource content shall be emptied during the write process.
+ * Do not use in conjunction with the readOnly
option.
+ * Activating this option might improve overall memory consumption.
+ * This should be used in cases where this is the last access to the resource.
+ * E.g. the final write of a resource after all processing is finished.
+ * @param [options.readOnly] Whether the resource content shall be written read-only
+ * Do not use in conjunction with the drain
option.
+ * The written file will be used as the new source of this resources content.
+ * Therefore the written file should not be altered by any means.
+ * Activating this option might improve overall memory consumption.
+ * @returns Promise resolving once data has been written
*/
- _write(resource, options) {
+ _write(resource: ResourceInterface, options?: {drain?: boolean; readOnly?: boolean}) {
const resourcePath = resource.getPath();
const basePathMatch = resourcePath.match(this._basePathRegex);
diff --git a/lib/adapters/AbstractAdapter.js b/src/adapters/AbstractAdapter.ts
similarity index 69%
rename from lib/adapters/AbstractAdapter.js
rename to src/adapters/AbstractAdapter.ts
index 96cf4154..36c89023 100644
--- a/lib/adapters/AbstractAdapter.js
+++ b/src/adapters/AbstractAdapter.ts
@@ -4,29 +4,32 @@ const log = getLogger("resources:adapters:AbstractAdapter");
import {minimatch} from "minimatch";
import micromatch from "micromatch";
import AbstractReaderWriter from "../AbstractReaderWriter.js";
-import Resource from "../Resource.js";
+import Resource, {type Resource_Options, type LegacyResource, type ResourceInterface} from "../Resource.js";
+import type {Project} from "@ui5/project/specifications/Project";
+import type Trace from "../tracing/Trace.js";
+import {isMigratedResource} from "../utils/tsUtils.js";
/**
* Abstract Resource Adapter
- *
- * @abstract
- * @public
- * @class
- * @alias @ui5/fs/adapters/AbstractAdapter
- * @extends @ui5/fs/AbstractReaderWriter
*/
class AbstractAdapter extends AbstractReaderWriter {
+ _virBasePath: string;
+ _virBaseDir: string;
+ _excludes: string[];
+ _excludesNegated: string[];
+ _project?: Project;
+
/**
* The constructor
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.virBasePath
+ * @param parameters Parameters
+ * @param parameters.virBasePath
* Virtual base path. Must be absolute, POSIX-style, and must end with a slash
- * @param {string[]} [parameters.excludes] List of glob patterns to exclude
- * @param {object} [parameters.project] Experimental, internal parameter. Do not use
+ * @param [parameters.excludes] List of glob patterns to exclude
+ * @param [parameters.project] Experimental, internal parameter. Do not use
*/
- constructor({virBasePath, excludes = [], project}) {
+ constructor({virBasePath, excludes = [], project}:
+ {virBasePath: string; excludes?: string[]; project?: Project}) {
if (new.target === AbstractAdapter) {
throw new TypeError("Class 'AbstractAdapter' is abstract");
}
@@ -48,19 +51,18 @@ class AbstractAdapter extends AbstractReaderWriter {
this._excludesNegated = excludes.map((pattern) => `!${pattern}`);
this._project = project;
}
+
/**
* Locates resources by glob.
*
- * @abstract
- * @private
- * @param {string|string[]} virPattern glob pattern as string or an array of
+ * @param virPattern glob pattern as string or an array of
* glob patterns for virtual directory structure
- * @param {object} [options={}] glob options
- * @param {boolean} [options.nodir=true] Do not match directories
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
+ * @param [options] glob options
+ * @param [options.nodir] Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
*/
- async _byGlob(virPattern, options = {nodir: true}, trace) {
+ async _byGlob(virPattern: string | string[], options = {nodir: true}, trace: Trace): Promise {
const excludes = this._excludesNegated;
if (!(virPattern instanceof Array)) {
@@ -69,8 +71,8 @@ class AbstractAdapter extends AbstractReaderWriter {
// Append static exclude patterns
virPattern = Array.prototype.concat.apply(virPattern, excludes);
- let patterns = virPattern.map(this._normalizePattern, this);
- patterns = Array.prototype.concat.apply([], patterns);
+ const normalizedPatterns = virPattern.map(this._normalizePattern.bind(this));
+ const patterns = Array.prototype.concat.apply([], normalizedPatterns) as string[];
if (patterns.length === 0) {
return [];
}
@@ -83,15 +85,15 @@ class AbstractAdapter extends AbstractReaderWriter {
return [
this._createResource({
statInfo: { // TODO: make closer to fs stat info
- isDirectory: function() {
+ isDirectory: function () {
return true;
- }
+ },
},
source: {
- adapter: "Abstract"
+ adapter: "Abstract",
},
- path: subPath
- })
+ path: subPath,
+ }),
];
}
}
@@ -102,10 +104,10 @@ 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
+ * @param virPath Virtual Path
+ * @returns True if path is excluded, otherwise false
*/
- _isPathExcluded(virPath) {
+ _isPathExcluded(virPath: string[]) {
return micromatch(virPath, this._excludes).length > 0;
}
@@ -114,10 +116,10 @@ class AbstractAdapter extends AbstractReaderWriter {
* This means that it either starts with the virtual base path of the adapter
* or equals the base directory (base path without a trailing slash)
*
- * @param {string} virPath Virtual Path
- * @returns {boolean} True if path should be handled
+ * @param virPath Virtual Path
+ * @returns True if path should be handled
*/
- _isPathHandled(virPath) {
+ _isPathHandled(virPath: string) {
// Check whether path starts with base path, or equals base directory
return virPath.startsWith(this._virBasePath) || virPath === this._virBaseDir;
}
@@ -125,29 +127,27 @@ class AbstractAdapter extends AbstractReaderWriter {
/**
* Normalizes virtual glob patterns.
*
- * @private
- * @param {string} virPattern glob pattern for virtual directory structure
- * @returns {string[]} A list of normalized glob patterns
+ * @param virPattern glob pattern for virtual directory structure
+ * @returns A list of normalized glob patterns
*/
- _normalizePattern(virPattern) {
- const that = this;
+ _normalizePattern(virPattern: string) {
const mm = new minimatch.Minimatch(virPattern);
const basePathParts = this._virBaseDir.split("/");
- function matchSubset(subset) {
+ const matchSubset = (subset: (string | typeof minimatch.GLOBSTAR | RegExp | undefined)[]) => {
let i;
for (i = 0; i < basePathParts.length; i++) {
const globPart = subset[i];
if (globPart === undefined) {
log.verbose("Ran out of glob parts to match (this should not happen):");
- if (that._project) { // project is optional
- log.verbose(`Project: ${that._project.getName()}`);
+ if (this._project) { // project is optional
+ log.verbose(`Project: ${this._project.getName()}`);
}
- log.verbose(`Virtual base path: ${that._virBaseDir}`);
+ log.verbose(`Virtual base path: ${this._virBaseDir}`);
log.verbose(`Pattern to match: ${virPattern}`);
log.verbose(`Current subset (tried index ${i}):`);
- log.verbose(subset);
+ log.verbose(String(subset));
return {idx: i, virtualMatch: true};
}
const basePathPart = basePathParts[i];
@@ -171,9 +171,9 @@ class AbstractAdapter extends AbstractReaderWriter {
return {rootMatch: true};
}
return {idx: i};
- }
+ };
- const resultGlobs = [];
+ const resultGlobs: string[] = [];
for (let i = 0; i < mm.set.length; i++) {
const match = matchSubset(mm.set[i]);
if (match) {
@@ -197,34 +197,34 @@ class AbstractAdapter extends AbstractReaderWriter {
return resultGlobs;
}
- _createResource(parameters) {
+ _createResource(parameters: Resource_Options): ResourceInterface {
if (this._project) {
parameters.project = this._project;
}
return new Resource(parameters);
}
- _migrateResource(resource) {
+ _migrateResource(resource: LegacyResource | ResourceInterface): Promise | ResourceInterface {
// This function only returns a promise if a migration is necessary.
// Since this is rarely the case, we therefore reduce the amount of
// created Promises by making this differentiation
// Check if its a fs/Resource v3, function 'hasProject' was
// introduced with v3 therefore take it as the indicator
- if (resource.hasProject) {
+ if (isMigratedResource(resource)) {
return resource;
}
return this._createFromLegacyResource(resource);
}
- async _createFromLegacyResource(resource) {
+ async _createFromLegacyResource(resource: LegacyResource): Promise {
const options = {
path: resource._path,
statInfo: resource._statInfo,
- source: resource._source
- };
+ source: resource._source,
+ } as Resource_Options;
- if (resource._stream) {
+ if (resource._stream && resource._getBufferFromStream) {
options.buffer = await resource._getBufferFromStream();
} else if (resource._createStream) {
options.createStream = resource._createStream;
@@ -234,14 +234,14 @@ class AbstractAdapter extends AbstractReaderWriter {
return new Resource(options);
}
- _assignProjectToResource(resource) {
+ _assignProjectToResource(resource: ResourceInterface) {
if (this._project) {
// 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.getProject()?.getName()} into adapter of project ${this._project.getName()}: ` +
resource.getPath());
}
return;
@@ -251,7 +251,7 @@ class AbstractAdapter extends AbstractReaderWriter {
}
}
- _resolveVirtualPathToBase(inputVirPath, writeMode = false) {
+ _resolveVirtualPathToBase(inputVirPath: string, writeMode = false): string | null {
if (!path.isAbsolute(inputVirPath)) {
throw new Error(`Failed to resolve virtual path '${inputVirPath}': Path must be absolute`);
}
@@ -265,15 +265,15 @@ class AbstractAdapter extends AbstractReaderWriter {
if (!this._isPathHandled(virPath)) {
if (log.isLevelEnabled("silly")) {
log.silly(`Failed to resolve virtual path '${inputVirPath}': ` +
- `Resolved path does not start with adapter base path '${this._virBasePath}' or equals ` +
- `base dir: ${this._virBaseDir}`);
+ `Resolved path does not start with adapter base path '${this._virBasePath}' or equals ` +
+ `base dir: ${this._virBaseDir}`);
}
return null;
}
- if (this._isPathExcluded(virPath)) {
+ if (this._isPathExcluded(Array.isArray(virPath) ? virPath : [virPath])) {
if (log.isLevelEnabled("silly")) {
log.silly(`Failed to resolve virtual path '${inputVirPath}': ` +
- `Resolved path is excluded by configuration of adapter with base path '${this._virBasePath}'`);
+ `Resolved path is excluded by configuration of adapter with base path '${this._virBasePath}'`);
}
return null;
}
diff --git a/lib/adapters/FileSystem.js b/src/adapters/FileSystem.ts
similarity index 77%
rename from lib/adapters/FileSystem.js
rename to src/adapters/FileSystem.ts
index f7862caa..eb3f7919 100644
--- a/lib/adapters/FileSystem.js
+++ b/src/adapters/FileSystem.ts
@@ -7,35 +7,41 @@ const copyFile = promisify(fs.copyFile);
const chmod = promisify(fs.chmod);
const mkdir = promisify(fs.mkdir);
const stat = promisify(fs.stat);
-import {globby, isGitIgnored} from "globby";
+import {globby, type GlobbyFilterFunction, isGitIgnored} from "globby";
import {PassThrough} from "node:stream";
import AbstractAdapter from "./AbstractAdapter.js";
+import type {Project} from "@ui5/project/specifications/Project";
+import type Trace from "../tracing/Trace.js";
+import {type LegacyResource, type Resource_Options, type ResourceInterface} from "../Resource.js";
+import {isError} from "../utils/tsUtils.js";
const READ_ONLY_MODE = 0o444;
const ADAPTER_NAME = "FileSystem";
+
/**
* File system resource adapter
- *
- * @public
- * @class
- * @alias @ui5/fs/adapters/FileSystem
- * @extends @ui5/fs/adapters/AbstractAdapter
*/
class FileSystem extends AbstractAdapter {
+ _fsBasePath: string;
+ _useGitignore: boolean;
+ _isGitIgnored!: GlobbyFilterFunction;
+
/**
* The Constructor.
*
- * @param {object} parameters Parameters
- * @param {string} parameters.virBasePath
+ * @param parameters Parameters
+ * @param parameters.virBasePath
* Virtual base path. Must be absolute, POSIX-style, and must end with a slash
- * @param {string} parameters.fsBasePath
+ * @param parameters.fsBasePath
* File System base path. Must be absolute and must use platform-specific path segment separators
- * @param {string[]} [parameters.excludes] List of glob patterns to exclude
- * @param {object} [parameters.useGitignore=false]
+ * @param [parameters.excludes] List of glob patterns to exclude
+ * @param [parameters.useGitignore]
* Whether to apply any excludes defined in an optional .gitignore in the given fsBasePath
directory
- * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any)
+ * @param [parameters.project] Project this adapter belongs to (if any)
*/
- constructor({virBasePath, project, fsBasePath, excludes, useGitignore=false}) {
+ constructor({virBasePath, project, fsBasePath, excludes, useGitignore = false}:
+ {virBasePath: string; project?: Project; fsBasePath: string; excludes?: string[]; useGitignore?: boolean}
+ ) {
super({virBasePath, project, excludes});
if (!fsBasePath) {
@@ -51,14 +57,13 @@ class FileSystem extends AbstractAdapter {
/**
* Locate resources by glob.
*
- * @private
- * @param {Array} patterns Array of glob patterns
- * @param {object} [options={}] glob options
- * @param {boolean} [options.nodir=true] Do not match directories
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
+ * @param patterns Array of glob patterns
+ * @param [options] glob options
+ * @param [options.nodir] Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
*/
- async _runGlob(patterns, options = {nodir: true}, trace) {
+ async _runGlob(patterns: string[], options = {nodir: true}, trace: Trace) {
const opt = {
cwd: this._fsBasePath,
dot: true,
@@ -68,7 +73,7 @@ class FileSystem extends AbstractAdapter {
};
trace.globCall();
- const promises = [];
+ const promises: Promise[] = [];
if (!opt.onlyFiles && patterns.includes("")) { // Match physical root directory
promises.push(new Promise((resolve, reject) => {
fs.stat(this._fsBasePath, (err, stat) => {
@@ -81,11 +86,11 @@ class FileSystem extends AbstractAdapter {
path: this._virBaseDir,
sourceMetadata: {
adapter: ADAPTER_NAME,
- fsPath: this._fsBasePath
+ fsPath: this._fsBasePath,
},
createStream: () => {
return fs.createReadStream(this._fsBasePath);
- }
+ },
}));
}
});
@@ -112,7 +117,7 @@ class FileSystem extends AbstractAdapter {
`the configured virtual base path of the adapter. Base path: '${this._virBasePath}'`);
resolve(null);
}
- const fsPath = this._resolveToFileSystem(relPath);
+ const fsPath = this._resolveToFileSystem(relPath ?? "");
// Workaround for not getting the stat from the glob
fs.stat(fsPath, (err, stat) => {
@@ -125,11 +130,11 @@ class FileSystem extends AbstractAdapter {
path: virPath,
sourceMetadata: {
adapter: ADAPTER_NAME,
- fsPath: fsPath
+ fsPath: fsPath,
},
createStream: () => {
return fs.createReadStream(fsPath);
- }
+ },
}));
}
});
@@ -139,19 +144,19 @@ class FileSystem extends AbstractAdapter {
const results = await Promise.all(promises);
// Flatten results
- return Array.prototype.concat.apply([], results).filter(($) => $);
+ return Array.prototype.concat.apply([], results).filter(($) => $) as ResourceInterface[];
}
/**
* Locate a resource by path.
*
- * @private
- * @param {string} virPath Absolute virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource or null if not found
+ * @param virPath Absolute virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to a single resource or null if not found
*/
- async _byPath(virPath, options, trace) {
+ async _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
const relPath = this._resolveVirtualPathToBase(virPath);
if (relPath === null) {
@@ -162,11 +167,11 @@ class FileSystem extends AbstractAdapter {
return this._createResource({
project: this._project,
statInfo: { // TODO: make closer to fs stat info
- isDirectory: function() {
+ isDirectory: function () {
return true;
- }
+ },
},
- path: virPath
+ path: virPath,
});
} else {
return null;
@@ -180,7 +185,7 @@ class FileSystem extends AbstractAdapter {
if (this._useGitignore) {
if (!this._isGitIgnored) {
this._isGitIgnored = await isGitIgnored({
- cwd: this._fsBasePath
+ cwd: this._fsBasePath,
});
}
// Check whether path should be ignored
@@ -195,26 +200,26 @@ class FileSystem extends AbstractAdapter {
if (options.nodir && statInfo.isDirectory()) {
return null;
}
- const resourceOptions = {
+ const resourceOptions: Resource_Options = {
project: this._project,
statInfo,
path: virPath,
sourceMetadata: {
adapter: ADAPTER_NAME,
- fsPath
- }
+ fsPath,
+ },
};
if (!statInfo.isDirectory()) {
// Add content as lazy stream
- resourceOptions.createStream = function() {
+ resourceOptions.createStream = function () {
return fs.createReadStream(fsPath);
};
}
return this._createResource(resourceOptions);
} catch (err) {
- if (err.code === "ENOENT") { // "File or directory does not exist"
+ if (isError(err) && err.code === "ENOENT") { // "File or directory does not exist"
return null;
} else {
throw err;
@@ -225,27 +230,30 @@ class FileSystem extends AbstractAdapter {
/**
* Writes the content of a resource to a path.
*
- * @private
- * @param {@ui5/fs/Resource} resource Resource to write
- * @param {object} [options]
- * @param {boolean} [options.readOnly] Whether the resource content shall be written read-only
+ * @param anyResource Resource to write
+ * @param [options] Writer options
+ * @param [options.readOnly] Whether the resource content shall be written read-only
* Do not use in conjunction with the drain
option.
* The written file will be used as the new source of this resources content.
* Therefore the written file should not be altered by any means.
* Activating this option might improve overall memory consumption.
- * @param {boolean} [options.drain] Whether the resource content shall be emptied during the write process.
+ * @param [options.drain] Whether the resource content shall be emptied during the write process.
* Do not use in conjunction with the readOnly
option.
* Activating this option might improve overall memory consumption.
* This should be used in cases where this is the last access to the resource.
* E.g. the final write of a resource after all processing is finished.
- * @returns {Promise} Promise resolving once data has been written
+ * @returns Promise resolving once data has been written
*/
- async _write(resource, {drain, readOnly}) {
- resource = this._migrateResource(resource);
- if (resource instanceof Promise) {
+ async _write(anyResource: LegacyResource | ResourceInterface,
+ {drain, readOnly}: {drain?: boolean; readOnly?: boolean}) {
+ const potentialResourceP = this._migrateResource(anyResource);
+ let resource: ResourceInterface;
+ if (potentialResourceP instanceof Promise) {
// Only await if the migrate function returned a promise
// Otherwise await would automatically create a Promise, causing unwanted overhead
- resource = await resource;
+ resource = await potentialResourceP;
+ } else {
+ resource = potentialResourceP;
}
this._assignProjectToResource(resource);
if (drain && readOnly) {
@@ -253,7 +261,7 @@ class FileSystem extends AbstractAdapter {
"Do not use options 'drain' and 'readOnly' at the same time.");
}
- const relPath = this._resolveVirtualPathToBase(resource.getPath(), true);
+ const relPath = this._resolveVirtualPathToBase(resource.getPath(), true) ?? "";
const fsPath = this._resolveToFileSystem(relPath);
const dirPath = path.dirname(fsPath);
@@ -307,11 +315,11 @@ class FileSystem extends AbstractAdapter {
} else {
// Transform stream into buffer before writing
contentStream = new PassThrough();
- const buffers = [];
+ const buffers: Buffer[] = [];
contentStream.on("error", (err) => {
reject(err);
});
- contentStream.on("data", (data) => {
+ contentStream.on("data", (data: Buffer) => {
buffers.push(data);
});
contentStream.on("end", () => {
@@ -321,7 +329,7 @@ class FileSystem extends AbstractAdapter {
resource.getStream().pipe(contentStream);
}
- const writeOptions = {};
+ const writeOptions: {mode?: number} = {};
if (readOnly) {
writeOptions.mode = READ_ONLY_MODE;
}
@@ -330,8 +338,8 @@ class FileSystem extends AbstractAdapter {
write.on("error", (err) => {
reject(err);
});
- write.on("close", (ex) => {
- resolve();
+ write.on("close", () => {
+ resolve(undefined);
});
contentStream.pipe(write);
});
@@ -347,13 +355,13 @@ class FileSystem extends AbstractAdapter {
// This should be identical to buffering the resource content in memory, since the written file
// can not be modified.
// We chose this approach to be more memory efficient in scenarios where readOnly is used
- resource.setStream(function() {
+ resource.setStream(function () {
return fs.createReadStream(fsPath);
});
}
}
- _resolveToFileSystem(relPath) {
+ _resolveToFileSystem(relPath: string) {
const fsPath = path.join(this._fsBasePath, relPath);
if (!fsPath.startsWith(this._fsBasePath)) {
diff --git a/lib/adapters/Memory.js b/src/adapters/Memory.ts
similarity index 53%
rename from lib/adapters/Memory.js
rename to src/adapters/Memory.ts
index 35be99cf..af6d71dc 100644
--- a/lib/adapters/Memory.js
+++ b/src/adapters/Memory.ts
@@ -2,56 +2,56 @@ import {getLogger} from "@ui5/logger";
const log = getLogger("resources:adapters:Memory");
import micromatch from "micromatch";
import AbstractAdapter from "./AbstractAdapter.js";
+import {type Project} from "@ui5/project/specifications/Project";
+import {type LegacyResource, type ResourceInterface} from "../Resource.js";
+import type Trace from "../tracing/Trace.js";
const ADAPTER_NAME = "Memory";
/**
* Virtual resource Adapter
- *
- * @public
- * @class
- * @alias @ui5/fs/adapters/Memory
- * @extends @ui5/fs/adapters/AbstractAdapter
*/
class Memory extends AbstractAdapter {
+ _virFiles: Record;
+ _virDirs: Record;
+
/**
* The constructor.
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.virBasePath
+ * @param parameters Parameters
+ * @param parameters.virBasePath
* Virtual base path. Must be absolute, POSIX-style, and must end with a slash
- * @param {string[]} [parameters.excludes] List of glob patterns to exclude
- * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any)
+ * @param [parameters.excludes] List of glob patterns to exclude
+ * @param [parameters.project] Project this adapter belongs to (if any)
*/
- constructor({virBasePath, project, excludes}) {
+ constructor({virBasePath, project, excludes}: {virBasePath: string; project?: Project; excludes?: string[]}) {
super({virBasePath, project, excludes});
- this._virFiles = Object.create(null); // map full of files
- this._virDirs = Object.create(null); // map full of directories
+ this._virFiles = Object.create(null) as Record; // map full of files
+ this._virDirs = Object.create(null) as Record; // map full of directories
}
/**
* Matches and returns resources from a given map (either _virFiles or _virDirs).
*
- * @private
- * @param {string[]} patterns
- * @param {object} resourceMap
- * @returns {Promise}
+ * @param patterns glob patterns
+ * @param resourceMap Resources cache
+ * @returns
*/
- async _matchPatterns(patterns, resourceMap) {
+ async _matchPatterns(patterns: string[],
+ resourceMap: Record): Promise {
const resourcePaths = Object.keys(resourceMap);
const matchedPaths = micromatch(resourcePaths, patterns, {
- dot: true
+ dot: true,
});
return await Promise.all(matchedPaths.map((virPath) => {
- const resource = resourceMap[virPath];
+ const resource: ResourceInterface = resourceMap[virPath];
if (resource) {
return this._cloneResource(resource);
}
- }));
+ }).filter(($) => !!$));
}
- async _cloneResource(resource) {
+ async _cloneResource(resource: ResourceInterface): Promise {
const clonedResource = await resource.clone();
if (this._project) {
clonedResource.setProject(this._project);
@@ -62,28 +62,27 @@ class Memory extends AbstractAdapter {
/**
* Locate resources by glob.
*
- * @private
- * @param {Array} patterns array of glob patterns
- * @param {object} [options={}] glob options
- * @param {boolean} [options.nodir=true] Do not match directories
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
+ * @param patterns array of glob patterns
+ * @param [options] glob options
+ * @param [options.nodir] Do not match directories
+ * @param _trace Trace instance
+ * @returns Promise resolving to list of resources
*/
- async _runGlob(patterns, options = {nodir: true}, trace) {
+ async _runGlob(patterns: string[], options = {nodir: true}, _trace: Trace) {
if (patterns[0] === "" && !options.nodir) { // Match virtual root directory
return [
this._createResource({
project: this._project,
statInfo: { // TODO: make closer to fs stat info
- isDirectory: function() {
+ isDirectory: function () {
return true;
- }
+ },
},
sourceMetadata: {
- adapter: ADAPTER_NAME
+ adapter: ADAPTER_NAME,
},
- path: this._virBasePath.slice(0, -1)
- })
+ path: this._virBasePath.slice(0, -1),
+ }),
];
}
@@ -100,13 +99,13 @@ class Memory extends AbstractAdapter {
/**
* Locates resources by path.
*
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing.Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to a single resource
*/
- async _byPath(virPath, options, trace) {
+ async _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
const relPath = this._resolveVirtualPathToBase(virPath);
if (relPath === null) {
return null;
@@ -114,9 +113,9 @@ class Memory extends AbstractAdapter {
trace.pathCall();
- const resource = this._virFiles[relPath];
+ const resource: ResourceInterface = this._virFiles[relPath];
- if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) {
+ if (!resource || (options.nodir && resource.getStatInfo().isDirectory?.())) {
return null;
} else {
return await this._cloneResource(resource);
@@ -126,25 +125,27 @@ class Memory extends AbstractAdapter {
/**
* Writes the content of a resource to a path.
*
- * @private
- * @param {@ui5/fs/Resource} resource The Resource to write
- * @returns {Promise} Promise resolving once data has been written
+ * @param anyResource The Resource to write
+ * @returns Promise resolving once data has been written
*/
- async _write(resource) {
- resource = this._migrateResource(resource);
- if (resource instanceof Promise) {
+ async _write(anyResource: ResourceInterface | LegacyResource) {
+ const migratedResource = this._migrateResource(anyResource);
+ let resource: ResourceInterface;
+ if (migratedResource instanceof Promise) {
// Only await if the migrate function returned a promise
// Otherwise await would automatically create a Promise, causing unwanted overhead
- resource = await resource;
+ resource = await migratedResource;
+ } else {
+ resource = migratedResource;
}
this._assignProjectToResource(resource);
const relPath = this._resolveVirtualPathToBase(resource.getPath(), true);
log.silly(`Writing to virtual path ${resource.getPath()}`);
- this._virFiles[relPath] = await resource.clone();
+ this._virFiles[relPath!] = await resource.clone();
// Add virtual directories for all path segments of the written resource
// TODO: Add tests for all this
- const pathSegments = relPath.split("/");
+ const pathSegments = relPath!.split("/");
pathSegments.pop(); // Remove last segment representing the resource itself
pathSegments.forEach((segment, i) => {
@@ -160,14 +161,14 @@ class Memory extends AbstractAdapter {
this._virDirs[segment] = this._createResource({
project: this._project,
sourceMetadata: {
- adapter: ADAPTER_NAME
+ adapter: ADAPTER_NAME,
},
statInfo: { // TODO: make closer to fs stat info
- isDirectory: function() {
+ isDirectory: function () {
return true;
- }
+ },
},
- path: this._virBasePath + segment
+ path: this._virBasePath + segment,
});
}
}
diff --git a/src/fsInterface.ts b/src/fsInterface.ts
new file mode 100644
index 00000000..bdaece5b
--- /dev/null
+++ b/src/fsInterface.ts
@@ -0,0 +1,114 @@
+import type AbstractReader from "./AbstractReader.js";
+import type * as fs from "node:fs";
+import {type Buffer} from "node:buffer";
+
+/**
+ *
+ * @param inputPath Path to convert to POSIX
+ */
+function toPosix(inputPath: string) {
+ return inputPath.replace(/\\/g, "/");
+}
+
+// Define the types for the options parameter
+type Read_File_Options = {encoding?: string} | string | undefined;
+
+// Define the types for the callback functions
+type Read_File_Callback = (err: Error | null, data?: Buffer | string) => void;
+type Stat_Callback = (err: Error | null, stats?: fs.Stats) => void;
+type Readdir_Callback = (err: Error | null, files?: string[]) => void;
+type Mkdir_Callback = (err?: Error | null) => void;
+
+/**
+ */
+interface File_System_Interface {
+ readFile: (fsPath: string, options: Read_File_Options, callback: Read_File_Callback) => void;
+ stat: (fsPath: string, callback: Stat_Callback) => void;
+ readdir: (fsPath: string, callback: Readdir_Callback) => void;
+ mkdir: (fsPath: string, callback: Mkdir_Callback) => void;
+};
+
+/**
+ * Wraps readers to access them through a [Node.js fs]{@link https://nodejs.org/api/fs.html} styled interface.
+ *
+ * @param reader Resource Reader or Collection
+ *
+ * @returns Object with [Node.js fs]{@link https://nodejs.org/api/fs.html} styled functions
+ * [readFile
]{@link https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback},
+ * [stat
]{@link https://nodejs.org/api/fs.html#fs_fs_stat_path_options_callback},
+ * [readdir
]{@link https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback} and
+ * [mkdir
]{@link https://nodejs.org/api/fs.html#fs_fs_mkdir_path_options_callback}
+ */
+function fsInterface(reader: AbstractReader) {
+ const fileSystem: File_System_Interface = {
+ readFile(fsPath, options, callback) {
+ if (typeof options === "function") {
+ callback = options;
+ options = undefined;
+ }
+ if (typeof options === "string") {
+ options = {encoding: options};
+ }
+ const posixPath = toPosix(fsPath);
+ reader.byPath(posixPath, {
+ nodir: false,
+ }).then(function (resource) {
+ if (!resource) {
+ const error: NodeJS.ErrnoException =
+ new Error(`ENOENT: no such file or directory, open '${fsPath}'`);
+ error.code = "ENOENT"; // "File or directory does not exist"
+ callback(error);
+ return;
+ }
+
+ return resource.getBuffer().then(function (buffer) {
+ let res;
+
+ if (options?.encoding) {
+ res = buffer.toString(options.encoding as BufferEncoding);
+ } else {
+ res = buffer;
+ }
+
+ callback(null, res);
+ });
+ }).catch(callback);
+ },
+ stat(fsPath, callback) {
+ const posixPath = toPosix(fsPath);
+ void reader.byPath(posixPath, {
+ nodir: false,
+ }).then(function (resource) {
+ if (!resource) {
+ const error: NodeJS.ErrnoException =
+ new Error(`ENOENT: no such file or directory, stat '${fsPath}'`);
+ error.code = "ENOENT"; // "File or directory does not exist"
+ callback(error);
+ } else {
+ callback(null, resource.getStatInfo() as fs.StatsBase);
+ }
+ }).catch(callback);
+ },
+ readdir(fsPath, callback) {
+ let posixPath = toPosix(fsPath);
+ if (!(/\/$/.exec(posixPath))) {
+ // Add trailing slash if not present
+ posixPath += "/";
+ }
+ void reader.byGlob(posixPath + "*", {
+ nodir: false,
+ }).then((resources) => {
+ const files = resources.map((resource) => {
+ return resource.getName();
+ });
+ callback(null, files);
+ }).catch(callback);
+ },
+ mkdir(_fsPath, callback) {
+ setTimeout(callback, 0);
+ },
+ };
+
+ return fileSystem;
+}
+export default fsInterface;
diff --git a/src/readers/Filter.ts b/src/readers/Filter.ts
new file mode 100644
index 00000000..0e0b63d2
--- /dev/null
+++ b/src/readers/Filter.ts
@@ -0,0 +1,78 @@
+import AbstractReader from "../AbstractReader.js";
+import {type ResourceInterface} from "../Resource.js";
+import type Trace from "../tracing/Trace.js";
+
+/**
+ * Filter callback
+ *
+ * @param resource Resource to test
+ * @returns Whether to keep the resource
+ */
+type Filter_Callback = (resource: ResourceInterface) => boolean;
+
+export interface Filter_Params {
+ reader: AbstractReader;
+ callback: Filter_Callback;
+};
+
+/**
+ * A reader that allows dynamic filtering of resources passed through it
+ */
+class Filter extends AbstractReader {
+ _reader: AbstractReader;
+ _callback: (resource: ResourceInterface) => boolean;
+
+ /**
+ * Constructor
+ *
+ * @param parameters Parameters
+ * @param parameters.reader The resource reader or collection to wrap
+ * @param parameters.callback
+ * Filter function. Will be called for every resource read through this reader.
+ */
+ constructor({reader, callback}: Filter_Params) {
+ super();
+ if (!reader) {
+ throw new Error(`Missing parameter "reader"`);
+ }
+ if (!callback) {
+ throw new Error(`Missing parameter "callback"`);
+ }
+ this._reader = reader;
+ this._callback = callback;
+ }
+
+ /**
+ * Locates resources by glob.
+ *
+ * @param pattern glob pattern as string or an array of
+ * glob patterns for virtual directory structure
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
+ */
+ async _byGlob(pattern: string | string[], options: {nodir: boolean}, trace: Trace) {
+ const result = await this._reader._byGlob(pattern, options, trace);
+ return result.filter(this._callback);
+ }
+
+ /**
+ * Locates resources by path.
+ *
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to a single resource
+ */
+ async _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
+ const result = await this._reader._byPath(virPath, options, trace);
+ if (result && !this._callback(result)) {
+ return null;
+ }
+ return result;
+ }
+}
+
+export default Filter;
diff --git a/lib/readers/Link.js b/src/readers/Link.ts
similarity index 56%
rename from lib/readers/Link.js
rename to src/readers/Link.ts
index dd2d40b3..01e9d44c 100644
--- a/lib/readers/Link.js
+++ b/src/readers/Link.ts
@@ -3,6 +3,15 @@ import ResourceFacade from "../ResourceFacade.js";
import {prefixGlobPattern} from "../resourceFactory.js";
import {getLogger} from "@ui5/logger";
const log = getLogger("resources:readers:Link");
+import type Trace from "../tracing/Trace.js";
+
+export interface Link_Args {
+ reader: AbstractReader;
+ pathMapping: {
+ linkPath: string;
+ targetPath: string;
+ };
+};
/**
* A reader that allows for rewriting paths segments of all resources passed through it.
@@ -20,31 +29,27 @@ const log = getLogger("resources:readers:Link");
* // The following resolves with a @ui5/fs/ResourceFacade of the resource
* // located at "/resources/my-app-name/Component.js" in the sourceReader
* const resource = await linkedReader.byPath("/app/Component.js");
- *
- * @public
- * @class
- * @alias @ui5/fs/readers/Link
- * @extends @ui5/fs/AbstractReader
*/
class Link extends AbstractReader {
+ _reader: AbstractReader;
+
/**
* Path mapping for a [Link]{@link @ui5/fs/readers/Link}
*
- * @public
- * @typedef {object} @ui5/fs/readers/Link/PathMapping
- * @property {string} linkPath Path to match and replace in the requested path or pattern
- * @property {string} targetPath Path to use as a replacement in the request for the source reader
+ * linkPath Path to match and replace in the requested path or pattern
+ *
+ * targetPath Path to use as a replacement in the request for the source reader
*/
+ _pathMapping: {linkPath: string; targetPath: string};
/**
* Constructor
*
- * @public
- * @param {object} parameters Parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader The resource reader or collection to wrap
- * @param {@ui5/fs/readers/Link/PathMapping} parameters.pathMapping
+ * @param parameters Parameters
+ * @param parameters.reader The resource reader or collection to wrap
+ * @param parameters.pathMapping Path mapping for a [Link]{@link @ui5/fs/readers/Link}
*/
- constructor({reader, pathMapping}) {
+ constructor({reader, pathMapping}: Link_Args) {
super();
if (!reader) {
throw new Error(`Missing parameter "reader"`);
@@ -60,18 +65,19 @@ class Link extends AbstractReader {
/**
* Locates resources by glob.
*
- * @private
- * @param {string|string[]} patterns glob pattern as string or an array of
+ * @param pattern glob pattern as string or an array of
* glob patterns for virtual directory structure
- * @param {object} options glob options
- * @param {@ui5/fs/tracing/Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
+ * @param options glob options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to list of resources
*/
- async _byGlob(patterns, options, trace) {
- if (!(patterns instanceof Array)) {
- patterns = [patterns];
+ async _byGlob(pattern: string | string[], options: {nodir: boolean}, trace: Trace) {
+ if (!(pattern instanceof Array)) {
+ pattern = [pattern];
}
- patterns = patterns.map((pattern) => {
+
+ pattern = pattern.flatMap((pattern) => {
if (pattern.startsWith(this._pathMapping.linkPath)) {
pattern = pattern.substr(this._pathMapping.linkPath.length);
}
@@ -79,31 +85,34 @@ class Link extends AbstractReader {
});
// Flatten prefixed patterns
- patterns = Array.prototype.concat.apply([], patterns);
+ pattern = Array.prototype.concat.apply([], pattern);
// Keep resource's internal path unchanged for now
- const resources = await this._reader._byGlob(patterns, options, trace);
- return resources.map((resource) => {
- const resourcePath = resource.getPath();
- if (resourcePath.startsWith(this._pathMapping.targetPath)) {
- return new ResourceFacade({
- resource,
- path: this._pathMapping.linkPath + resourcePath.substr(this._pathMapping.targetPath.length)
- });
- }
- });
+ const resources = await this._reader._byGlob(pattern, options, trace);
+
+ return resources
+ .map((resource) => {
+ const resourcePath = resource.getPath();
+ if (resourcePath.startsWith(this._pathMapping.targetPath)) {
+ return new ResourceFacade({
+ resource,
+ path: this._pathMapping.linkPath + resourcePath.substr(this._pathMapping.targetPath.length),
+ });
+ }
+ })
+ .filter((resource) => resource !== undefined);
}
/**
* Locates resources by path.
*
- * @private
- * @param {string} virPath Virtual path
- * @param {object} options Options
- * @param {@ui5/fs/tracing/Trace} trace Trace instance
- * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
+ * @param virPath Virtual path
+ * @param options Options
+ * @param options.nodir Do not match directories
+ * @param trace Trace instance
+ * @returns Promise resolving to a single resource
*/
- async _byPath(virPath, options, trace) {
+ async _byPath(virPath: string, options: {nodir: boolean}, trace: Trace) {
if (!virPath.startsWith(this._pathMapping.linkPath)) {
return null;
}
@@ -114,13 +123,13 @@ class Link extends AbstractReader {
if (resource) {
return new ResourceFacade({
resource,
- path: this._pathMapping.linkPath + resource.getPath().substr(this._pathMapping.targetPath.length)
+ path: this._pathMapping.linkPath + resource.getPath().substr(this._pathMapping.targetPath.length),
});
}
return null;
}
- static _validatePathMapping({linkPath, targetPath}) {
+ static _validatePathMapping({linkPath, targetPath}: {linkPath: string; targetPath: string}) {
if (!linkPath) {
throw new Error(`Path mapping is missing attribute "linkPath"`);
}
diff --git a/lib/resourceFactory.js b/src/resourceFactory.ts
similarity index 56%
rename from lib/resourceFactory.js
rename to src/resourceFactory.ts
index ba48d76d..99da6188 100644
--- a/lib/resourceFactory.js
+++ b/src/resourceFactory.ts
@@ -5,17 +5,19 @@ import FsAdapter from "./adapters/FileSystem.js";
import MemAdapter from "./adapters/Memory.js";
import ReaderCollection from "./ReaderCollection.js";
import ReaderCollectionPrioritized from "./ReaderCollectionPrioritized.js";
-import Resource from "./Resource.js";
+import Resource, {type Resource_Options, type ResourceInterface} from "./Resource.js";
import WriterCollection from "./WriterCollection.js";
-import Filter from "./readers/Filter.js";
-import Link from "./readers/Link.js";
+import Filter, {type Filter_Params} from "./readers/Filter.js";
+import Link, {type Link_Args} from "./readers/Link.js";
import {getLogger} from "@ui5/logger";
+import {type Project} from "@ui5/project/specifications/Project";
+import type AbstractReader from "./AbstractReader.js";
+import type AbstractReaderWriter from "./AbstractReaderWriter.js";
const log = getLogger("resources:resourceFactory");
/**
* @module @ui5/fs/resourceFactory
* @description A collection of resource related APIs
- * @public
*/
/**
@@ -24,21 +26,22 @@ const log = getLogger("resources:resourceFactory");
* If a file system base path is given, file system resource ReaderWriter
is returned.
* In any other case a virtual one.
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash
- * @param {string} [parameters.fsBasePath]
+ * @param parameters Parameters
+ * @param parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash
+ * @param [parameters.fsBasePath]
* File System base path.
* If this parameter is supplied, a File System adapter will be created instead of a Memory adapter.
* The provided path must be absolute and must use platform-specific path segment separators.
- * @param {string[]} [parameters.excludes] List of glob patterns to exclude
- * @param {object} [parameters.useGitignore=false]
+ * @param [parameters.excludes] List of glob patterns to exclude
+ * @param [parameters.useGitignore]
* Whether to apply any excludes defined in an optional .gitignore in the base directory.
* This parameter only takes effect in conjunction with the fsBasePath
parameter.
- * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any)
- * @returns {@ui5/fs/adapters/FileSystem|@ui5/fs/adapters/Memory} File System- or Virtual Adapter
+ * @param [parameters.project] Project this adapter belongs to (if any)
+ * @returns File System- or Virtual Adapter
*/
-export function createAdapter({fsBasePath, virBasePath, project, excludes, useGitignore}) {
+export function createAdapter({fsBasePath, virBasePath, project, excludes, useGitignore}:
+{fsBasePath?: string; virBasePath: string; project?: Project; excludes?: string[]; useGitignore?: boolean}
+) {
if (fsBasePath) {
return new FsAdapter({fsBasePath, virBasePath, project, excludes, useGitignore});
} else {
@@ -49,17 +52,18 @@ export function createAdapter({fsBasePath, virBasePath, project, excludes, useGi
/**
* Creates a File System adapter and wraps it in a ReaderCollection
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash
- * @param {string} parameters.fsBasePath
+ * @param parameters Parameters
+ * @param parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash
+ * @param parameters.fsBasePath
* File System base path. Must be absolute and must use platform-specific path segment separators
- * @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 {@ui5/fs/ReaderCollection} Reader collection wrapping an adapter
+ * @param [parameters.project] Experimental, internal parameter. Do not use
+ * @param [parameters.excludes] List of glob patterns to exclude
+ * @param [parameters.name] Name for the reader collection
+ * @returns Reader collection wrapping an adapter
*/
-export function createReader({fsBasePath, virBasePath, project, excludes = [], name}) {
+export function createReader({fsBasePath, virBasePath, project, excludes = [], name}:
+{fsBasePath: string; virBasePath: string; project?: Project; excludes?: string[]; name?: string}
+) {
if (!fsBasePath) {
// Creating a reader with a memory adapter seems pointless right now
// since there would be no way to fill the adapter with resources
@@ -73,7 +77,7 @@ export function createReader({fsBasePath, virBasePath, project, excludes = [], n
// ui5 runtime path of the excluded resources. Therefore, only allow paths like /resources//test
// starting with specVersion 4.0
if (excludes.length && project && project.getType() === "application") {
- normalizedExcludes = excludes.map((pattern) => {
+ const nestedNormalizedExcludes = excludes.map((pattern) => {
if (pattern.startsWith(virBasePath) || pattern.startsWith("!" + virBasePath)) {
return pattern;
}
@@ -82,9 +86,9 @@ export function createReader({fsBasePath, virBasePath, project, excludes = [], n
return prefixGlobPattern(pattern, virBasePath);
});
// Flatten list of patterns
- normalizedExcludes = Array.prototype.concat.apply([], normalizedExcludes);
+ normalizedExcludes = Array.prototype.concat.apply([], nestedNormalizedExcludes) as string[];
log.verbose(`Effective exclude patterns for application project ${project.getName()}:\n` +
- normalizedExcludes.join(", "));
+ normalizedExcludes.join(", "));
}
return new ReaderCollection({
name,
@@ -92,58 +96,56 @@ export function createReader({fsBasePath, virBasePath, project, excludes = [], n
fsBasePath,
virBasePath,
project,
- excludes: normalizedExcludes
- })]
+ excludes: normalizedExcludes,
+ })],
});
}
/**
* Creates a ReaderCollection
*
- * @public
- * @param {object} parameters Parameters
- * @param {string} parameters.name The collection name
- * @param {@ui5/fs/AbstractReader[]} parameters.readers List of resource readers (all tried in parallel)
- * @returns {@ui5/fs/ReaderCollection} Reader collection wrapping provided readers
+ * @param parameters Parameters
+ * @param parameters.name The collection name
+ * @param parameters.readers List of resource readers (all tried in parallel)
+ * @returns Reader collection wrapping provided readers
*/
-export function createReaderCollection({name, readers}) {
+export function createReaderCollection({name, readers}: {name: string; readers: AbstractReader[]}) {
return new ReaderCollection({
name,
- readers
+ readers,
});
}
/**
* Creates a ReaderCollectionPrioritized
*
- * @public
- * @param {object} parameters
- * @param {string} parameters.name The collection name
- * @param {@ui5/fs/AbstractReader[]} parameters.readers Prioritized list of resource readers
+ * @param parameters Parameters
+ * @param parameters.name The collection name
+ * @param parameters.readers Prioritized list of resource readers
* (first is tried first)
- * @returns {@ui5/fs/ReaderCollectionPrioritized} Reader collection wrapping provided readers
+ * @returns Reader collection wrapping provided readers
*/
-export function createReaderCollectionPrioritized({name, readers}) {
+export function createReaderCollectionPrioritized({name, readers}: {name: string; readers: AbstractReader[]}) {
return new ReaderCollectionPrioritized({
name,
- readers
+ readers,
});
}
/**
* Creates a WriterCollection
*
- * @public
- * @param {object} parameters
- * @param {string} parameters.name The collection name
- * @param {object.} parameters.writerMapping Mapping of virtual base
+ * @param parameters Parameters
+ * @param parameters.name The collection name
+ * @param parameters.writerMapping Mapping of virtual base
* paths to writers. Path are matched greedy
- * @returns {@ui5/fs/WriterCollection} Writer collection wrapping provided writers
+ * @returns Writer collection wrapping provided writers
*/
-export function createWriterCollection({name, writerMapping}) {
+export function createWriterCollection({name, writerMapping}:
+{name: string; writerMapping: Record}) {
return new WriterCollection({
name,
- writerMapping
+ writerMapping,
});
}
@@ -151,11 +153,10 @@ export function createWriterCollection({name, writerMapping}) {
* Creates a [Resource]{@link @ui5/fs/Resource}.
* Accepts the same parameters as the [Resource]{@link @ui5/fs/Resource} constructor.
*
- * @public
- * @param {object} parameters Parameters to be passed to the resource constructor
- * @returns {@ui5/fs/Resource} Resource
+ * @param parameters Parameters to be passed to the resource constructor
+ * @returns Resource
*/
-export function createResource(parameters) {
+export function createResource(parameters: Resource_Options): ResourceInterface {
return new Resource(parameters);
}
@@ -166,26 +167,27 @@ export function createResource(parameters) {
* to write modified files into a separate writer, this is usually a Memory adapter. If a file already exists it is
* fetched from the memory to work on it in further build steps.
*
- * @public
- * @param {object} parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers
- * @param {@ui5/fs/AbstractReaderWriter} [parameters.writer] A ReaderWriter instance which is
+ * @param parameters Parameters
+ * @param parameters.reader Single reader or collection of readers
+ * @param [parameters.writer] A ReaderWriter instance which is
* only used for writing files. If not supplied, a Memory adapter will be created.
- * @param {string} [parameters.name="workspace"] Name of the collection
- * @param {string} [parameters.virBasePath="/"] Virtual base path
- * @returns {@ui5/fs/DuplexCollection} DuplexCollection which wraps the provided resource locators
+ * @param [parameters.name] Name of the collection
+ * @param [parameters.virBasePath] Virtual base path
+ * @returns DuplexCollection which wraps the provided resource locators
*/
-export function createWorkspace({reader, writer, virBasePath = "/", name = "workspace"}) {
+export function createWorkspace({reader, writer, virBasePath = "/", name = "workspace"}:
+{reader: AbstractReader; writer?: AbstractReaderWriter; virBasePath?: string; name?: string}
+) {
if (!writer) {
writer = new MemAdapter({
- virBasePath
+ virBasePath,
});
}
return new DuplexCollection({
reader,
writer,
- name
+ name,
});
}
@@ -194,14 +196,13 @@ export function createWorkspace({reader, writer, virBasePath = "/", name = "work
* The provided callback is called for every resource that is retrieved through the
* reader and decides whether the resource shall be passed on or dropped.
*
- * @public
- * @param {object} parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers
- * @param {@ui5/fs/readers/Filter~callback} parameters.callback
+ * @param parameters Parameters
+ * @param parameters.reader Single reader or collection of readers
+ * @param parameters.callback
* Filter function. Will be called for every resource passed through this reader.
- * @returns {@ui5/fs/readers/Filter} Reader instance
+ * @returns Reader instance
*/
-export function createFilterReader(parameters) {
+export function createFilterReader(parameters: Filter_Params) {
return new Filter(parameters);
}
@@ -223,13 +224,14 @@ export function createFilterReader(parameters) {
* // located at "/resources/my-app-name/Component.js" in the sourceReader
* const resource = await linkedReader.byPath("/app/Component.js");
*
- * @public
- * @param {object} parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers
- * @param {@ui5/fs/readers/Link/PathMapping} parameters.pathMapping
- * @returns {@ui5/fs/readers/Link} Reader instance
+ * @param parameters Parameters
+ * @param parameters.reader Single reader or collection of readers
+ * @param parameters.pathMapping Path mapping for a [Link]{@link @ui5/fs/readers/Link}
+ * @param parameters.pathMapping.linkPath Path to match and replace in the requested path or pattern
+ * @param parameters.pathMapping.targetPath Path to use as a replacement in the request for the source reader
+ * @returns Reader instance
*/
-export function createLinkReader(parameters) {
+export function createLinkReader(parameters: Link_Args) {
return new Link(parameters);
}
@@ -240,19 +242,18 @@ export function createLinkReader(parameters) {
* This simulates "flat" resource access, which is for example common for projects of type
* "application".
*
- * @public
- * @param {object} parameters
- * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers
- * @param {string} parameters.namespace Project namespace
- * @returns {@ui5/fs/readers/Link} Reader instance
+ * @param parameters Parameters
+ * @param parameters.reader Single reader or collection of readers
+ * @param parameters.namespace Project namespace
+ * @returns Reader instance
*/
-export function createFlatReader({reader, namespace}) {
+export function createFlatReader({reader, namespace}: {reader: AbstractReader; namespace: string}) {
return new Link({
reader: reader,
pathMapping: {
linkPath: `/`,
- targetPath: `/resources/${namespace}/`
- }
+ targetPath: `/resources/${namespace}/`,
+ },
});
}
@@ -260,14 +261,15 @@ export function createFlatReader({reader, namespace}) {
* 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
+ * @param virPattern glob pattern for virtual directory structure
+ * @param virBaseDir virtual base directory path to prefix the given patterns with
+ * @returns A list of normalized glob patterns
*/
-export function prefixGlobPattern(virPattern, virBaseDir) {
+export function prefixGlobPattern(virPattern: string, virBaseDir: string): string[] {
const mm = new minimatch.Minimatch(virPattern);
const resultGlobs = [];
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < mm.globSet.length; i++) {
let resultPattern = path.posix.join(virBaseDir, mm.globSet[i]);
diff --git a/lib/tracing/Trace.js b/src/tracing/Trace.ts
similarity index 81%
rename from lib/tracing/Trace.js
rename to src/tracing/Trace.ts
index 30ac9e19..756ff0b0 100644
--- a/lib/tracing/Trace.js
+++ b/src/tracing/Trace.ts
@@ -3,17 +3,20 @@ const log = getLogger("resources:tracing:Trace");
const logGlobs = getLogger("resources:tracing:Trace:globs");
const logPaths = getLogger("resources:tracing:Trace:paths");
import prettyHrtime from "pretty-hrtime";
-import summaryTrace from "./traceSummary.js";
-const hasOwnProperty = Object.prototype.hasOwnProperty;
+import summaryTrace, {type CollectionsType} from "./traceSummary.js";
/**
* Trace
*
- * @private
- * @class
*/
class Trace {
- constructor(name) {
+ _name!: string;
+ _startTime!: [number, number];
+ _globCalls!: number;
+ _pathCalls!: number;
+ _collections!: CollectionsType;
+
+ constructor(name: string) {
if (!log.isLevelEnabled("silly")) {
return;
}
@@ -21,7 +24,7 @@ class Trace {
this._startTime = process.hrtime();
this._globCalls = 0;
this._pathCalls = 0;
- this._collections = Object.create(null);
+ this._collections = Object.create(null) as CollectionsType;
summaryTrace.traceStarted();
}
@@ -41,7 +44,7 @@ class Trace {
summaryTrace.pathCall();
}
- collection(name) {
+ collection(name: string) {
if (!log.isLevelEnabled("silly")) {
return;
}
@@ -50,7 +53,7 @@ class Trace {
this._collections[name].calls++;
} else {
this._collections[name] = {
- calls: 1
+ calls: 1,
};
}
summaryTrace.collection(name);
@@ -76,7 +79,7 @@ class Trace {
report += ` ${colCount} reader-collections involed:\n`;
for (const coll in this._collections) {
- if (hasOwnProperty.call(this._collections, coll)) {
+ if (Object.prototype.hasOwnProperty.call(this._collections, coll)) {
report += ` ${this._collections[coll].calls}x ${coll}\n`;
}
}
@@ -90,7 +93,7 @@ class Trace {
logPaths.silly(report);
}
- summaryTrace.traceEnded();
+ void summaryTrace.traceEnded();
}
}
diff --git a/lib/tracing/traceSummary.js b/src/tracing/traceSummary.ts
similarity index 65%
rename from lib/tracing/traceSummary.js
rename to src/tracing/traceSummary.ts
index 79f89378..2e23755c 100644
--- a/lib/tracing/traceSummary.js
+++ b/src/tracing/traceSummary.ts
@@ -2,31 +2,54 @@ import {getLogger} from "@ui5/logger";
const log = getLogger("resources:tracing:total");
import prettyHrtime from "pretty-hrtime";
-const hasOwnProperty = Object.prototype.hasOwnProperty;
-let timeoutId;
+let timeoutId: NodeJS.Timeout;
let active = false;
let tracesRunning = 0;
-let traceData;
+export type CollectionsType = Record>;
+
+let traceData: null | {
+ startTime: [number, number];
+ pathCalls: number;
+ globCalls: number;
+ collections: CollectionsType;
+ traceCalls: number;
+ timeDiff?: [number, number];
+};
+
+/**
+ *
+ */
function init() {
traceData = {
startTime: process.hrtime(),
pathCalls: 0,
globCalls: 0,
collections: {},
- traceCalls: 0
+ traceCalls: 0,
};
active = true;
}
+/**
+ *
+ */
function reset() {
traceData = null;
active = false;
}
+/**
+ *
+ */
function report() {
let report = "";
- const time = prettyHrtime(traceData.timeDiff);
+
+ if (!traceData) {
+ return;
+ }
+
+ const time = prettyHrtime(traceData.timeDiff!);
const colCount = Object.keys(traceData.collections).length;
report += "==========================\n[=> TRACE SUMMARY:\n";
@@ -41,7 +64,7 @@ function report() {
report += ` ${colCount} rl-collections involed:\n`;
for (const coll in traceData.collections) {
- if (hasOwnProperty.call(traceData.collections, coll)) {
+ if (Object.prototype.hasOwnProperty.call(traceData.collections, coll)) {
report += ` ${traceData.collections[coll].calls}x ${coll}\n`;
}
}
@@ -49,6 +72,9 @@ function report() {
log.silly(report);
}
+/**
+ *
+ */
function someTraceStarted() {
if (!log.isLevelEnabled("silly")) {
return;
@@ -57,15 +83,18 @@ function someTraceStarted() {
init();
}
tracesRunning++;
- traceData.traceCalls++;
+ traceData!.traceCalls++;
if (timeoutId) {
clearTimeout(timeoutId);
}
}
-function someTraceEnded() {
- return new Promise(function(resolve, reject) {
+/**
+ *
+ */
+function someTraceEnded(): Promise {
+ return new Promise(function (resolve) {
if (!active) {
resolve();
return;
@@ -79,8 +108,8 @@ function someTraceEnded() {
if (timeoutId) {
clearTimeout(timeoutId);
}
- traceData.timeDiff = process.hrtime(traceData.startTime);
- timeoutId = setTimeout(function() {
+ traceData!.timeDiff = process.hrtime(traceData!.startTime);
+ timeoutId = setTimeout(function () {
report();
reset();
resolve();
@@ -88,22 +117,37 @@ function someTraceEnded() {
});
}
+/**
+ *
+ */
function pathCall() {
if (!active) {
return;
}
- traceData.pathCalls++;
+ if (traceData) {
+ traceData.pathCalls++;
+ }
}
+/**
+ *
+ */
function globCall() {
if (!active) {
return;
}
- traceData.globCalls++;
+
+ if (traceData) {
+ traceData.globCalls++;
+ }
}
-function collection(name) {
- if (!active) {
+/**
+ *
+ * @param name TraceData collection name
+ */
+function collection(name: string) {
+ if (!active || !traceData) {
return;
}
const collection = traceData.collections[name];
@@ -111,7 +155,7 @@ function collection(name) {
traceData.collections[name].calls++;
} else {
traceData.collections[name] = {
- calls: 1
+ calls: 1,
};
}
}
@@ -121,5 +165,5 @@ export default {
globCall: globCall,
collection: collection,
traceStarted: someTraceStarted,
- traceEnded: someTraceEnded
+ traceEnded: someTraceEnded,
};
diff --git a/src/utils/mock-projects.d.ts b/src/utils/mock-projects.d.ts
new file mode 100644
index 00000000..60219504
--- /dev/null
+++ b/src/utils/mock-projects.d.ts
@@ -0,0 +1,21 @@
+// TODO: This file is meant only for temp resolve of the UI5 tooling
+// dependencies, until they got migrated and we can have the real TS definitions
+
+declare module "@ui5/project/specifications/Project" {
+
+ export interface Project {
+ getName: () => string;
+ getVersion: () => string;
+ getType: () => "project" | "application" | "library";
+ }
+}
+
+declare module "@ui5/logger" {
+ interface logger {
+ silly(x: string): void;
+ verbose(x: string): void;
+ isLevelEnabled(x: string): boolean;
+ }
+
+ export function getLogger(x: string): logger;
+}
diff --git a/src/utils/tsUtils.ts b/src/utils/tsUtils.ts
new file mode 100644
index 00000000..1a08f40b
--- /dev/null
+++ b/src/utils/tsUtils.ts
@@ -0,0 +1,28 @@
+import {type ResourceInterface} from "../Resource.js";
+import type Resource from "../Resource.js";
+
+/**
+ *
+ * @param testString Variable to test if it's a string type
+ */
+export function isString(testString: unknown): testString is string {
+ return testString instanceof String || String(testString) === testString;
+}
+
+/**
+ *
+ * @param resource Variable to test if it's with a Resource type
+ */
+export function isMigratedResource(resource: unknown): resource is Resource | ResourceInterface {
+ // Check if its a fs/Resource v3, function 'hasProject' was
+ // introduced with v3 therefore take it as the indicator
+ return !!resource && typeof resource === "object" && ("hasProject" in resource);
+}
+
+/**
+ *
+ * @param error Error to test
+ */
+export function isError(error: unknown): error is NodeJS.ErrnoException {
+ return error instanceof Error;
+}
diff --git a/src/utils/types/clone.d.ts b/src/utils/types/clone.d.ts
new file mode 100644
index 00000000..c6862463
--- /dev/null
+++ b/src/utils/types/clone.d.ts
@@ -0,0 +1,3 @@
+declare module "clone" {
+ export default function clone(arg: T): T;
+}
diff --git a/src/utils/types/graceful-fs.d.ts b/src/utils/types/graceful-fs.d.ts
new file mode 100644
index 00000000..ca3301bf
--- /dev/null
+++ b/src/utils/types/graceful-fs.d.ts
@@ -0,0 +1,6 @@
+// "graceful-fs" definitions just inherit the ones from node:fs, but are quite
+// inconvenient. So, proxying the ones from node:fs would be better.
+declare module "graceful-fs" {
+ import * as fs from "node:fs";
+ export default fs;
+}
diff --git a/test/lib/AbstractReader.js b/test/lib/AbstractReader.js
deleted file mode 100644
index c5d9c009..00000000
--- a/test/lib/AbstractReader.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import test from "ava";
-import AbstractReader from "../../lib/AbstractReader.js";
-
-test("AbstractReader: constructor throws an error", (t) => {
- t.throws(() => {
- new AbstractReader();
- }, {
- instanceOf: TypeError,
- message: "Class 'AbstractReader' is abstract"
- });
-});
-
-test("Incomplete AbstractReader subclass: Abstract functions throw error", (t) => {
- class Dummy extends AbstractReader {}
-
- const instance = new Dummy();
- t.throws(() => {
- instance._byGlob();
- }, {
- instanceOf: Error,
- message: "Function '_byGlob' is not implemented"
- });
-
- t.throws(() => {
- instance._runGlob();
- }, {
- instanceOf: Error,
- message: "Function '_runGlob' is not implemented"
- });
-
- t.throws(() => {
- instance._byPath();
- }, {
- instanceOf: Error,
- message: "Function '_byPath' is not implemented"
- });
-});
diff --git a/test/lib/AbstractReader.ts b/test/lib/AbstractReader.ts
new file mode 100644
index 00000000..1945ae51
--- /dev/null
+++ b/test/lib/AbstractReader.ts
@@ -0,0 +1,40 @@
+import test from "ava";
+import AbstractReader from "../../src/AbstractReader.js";
+
+test("AbstractReader: constructor throws an error", (t) => {
+ t.throws(() => {
+ new AbstractReader();
+ }, {
+ instanceOf: TypeError,
+ message: "Class 'AbstractReader' is abstract",
+ });
+});
+
+test("Incomplete AbstractReader subclass: Abstract functions throw error", async (t) => {
+ class Dummy extends AbstractReader {}
+
+ const instance = new Dummy();
+ await t.throwsAsync(async () => {
+ // @ts-expect-error testing invalid value
+ await instance._byGlob();
+ }, {
+ instanceOf: Error,
+ message: "Function '_byGlob' is not implemented",
+ });
+
+ await t.throwsAsync(async () => {
+ // @ts-expect-error testing invalid value
+ await instance._runGlob();
+ }, {
+ instanceOf: Error,
+ message: "Function '_runGlob' is not implemented",
+ });
+
+ await t.throwsAsync(async () => {
+ // @ts-expect-error testing invalid value
+ await instance._byPath();
+ }, {
+ instanceOf: Error,
+ message: "Function '_byPath' is not implemented",
+ });
+});
diff --git a/test/lib/AbstractReaderWriter.js b/test/lib/AbstractReaderWriter.ts
similarity index 65%
rename from test/lib/AbstractReaderWriter.js
rename to test/lib/AbstractReaderWriter.ts
index 1cb5cec2..a81b9a0e 100644
--- a/test/lib/AbstractReaderWriter.js
+++ b/test/lib/AbstractReaderWriter.ts
@@ -1,12 +1,12 @@
import test from "ava";
-import AbstractReaderWriter from "../../lib/AbstractReaderWriter.js";
+import AbstractReaderWriter from "../../src/AbstractReaderWriter.js";
test("AbstractReaderWriter: constructor throws an error", (t) => {
t.throws(() => {
new AbstractReaderWriter();
}, {
instanceOf: TypeError,
- message: "Class 'AbstractReaderWriter' is abstract"
+ message: "Class 'AbstractReaderWriter' is abstract",
});
});
@@ -16,10 +16,10 @@ test("Incomplete AbstractReaderWriter subclass: Abstract functions throw error",
const instance = new Dummy();
t.throws(() => {
- instance._write();
+ // @ts-expect-error testing invalid value
+ void instance._write();
}, {
instanceOf: Error,
- message: "Not implemented"
+ message: "Not implemented",
});
});
-
diff --git a/test/lib/DuplexCollection.js b/test/lib/DuplexCollection.ts
similarity index 85%
rename from test/lib/DuplexCollection.js
rename to test/lib/DuplexCollection.ts
index bba5b096..8d25e8b5 100644
--- a/test/lib/DuplexCollection.js
+++ b/test/lib/DuplexCollection.ts
@@ -1,14 +1,14 @@
import test from "ava";
import sinon from "sinon";
-import DuplexCollection from "../../lib/DuplexCollection.js";
-import ReaderCollectionPrioritized from "../../lib/ReaderCollectionPrioritized.js";
-import Resource from "../../lib/Resource.js";
+import DuplexCollection from "../../src/DuplexCollection.js";
+import ReaderCollectionPrioritized from "../../src/ReaderCollectionPrioritized.js";
+import Resource from "../../src/Resource.js";
test("DuplexCollection: constructor", (t) => {
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: {},
- writer: {}
+ writer: {},
});
t.deepEqual(duplexCollection._reader, {}, "reader assigned");
@@ -21,7 +21,7 @@ test("DuplexCollection: constructor", (t) => {
test("DuplexCollection: constructor with setting default name of an empty string", (t) => {
const duplexCollection = new DuplexCollection({
reader: {},
- writer: {}
+ writer: {},
});
t.deepEqual(duplexCollection._reader, {}, "reader assigned");
@@ -35,15 +35,15 @@ test("DuplexCollection: _byGlob w/o finding a resource", async (t) => {
t.plan(3);
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([]))
+ _byGlob: sinon.stub().returns(Promise.resolve([])),
};
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: abstractReader,
- writer: abstractReader
+ writer: abstractReader,
});
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const comboSpy = sinon.spy(duplexCollection._combo, "_byGlob");
@@ -60,18 +60,18 @@ test("DuplexCollection: _byGlob", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([resource]))
+ _byGlob: sinon.stub().returns(Promise.resolve([resource])),
};
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: abstractReader,
- writer: abstractReader
+ writer: abstractReader,
});
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const comboSpy = sinon.spy(duplexCollection._combo, "_byGlob");
const resources = await duplexCollection._byGlob("anyPattern", {someOption: true}, trace);
@@ -90,19 +90,19 @@ test("DuplexCollection: _byPath with reader finding a resource", async (t) => {
const resource = new Resource({
path: "/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const pushCollectionSpy = sinon.spy(resource, "pushCollection");
const abstractReader = {
- _byPath: sinon.stub().returns(Promise.resolve(resource))
+ _byPath: sinon.stub().returns(Promise.resolve(resource)),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: abstractReader,
- writer: abstractReader
+ writer: abstractReader,
});
const comboSpy = sinon.spy(duplexCollection._combo, "_byPath");
const readResource = await duplexCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -119,18 +119,18 @@ test("DuplexCollection: _byPath with two readers both finding no resource", asyn
t.plan(3);
const abstractReaderOne = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const abstractReaderTwo = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const trace = {
- collection: sinon.stub()
+ collection: sinon.stub(),
};
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: abstractReaderOne,
- writer: abstractReaderTwo
+ writer: abstractReaderTwo,
});
const readResource = await duplexCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -146,14 +146,14 @@ test("DuplexCollection: _write successful", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const duplexCollection = new DuplexCollection({
name: "myCollection",
reader: {},
writer: {
- write: sinon.stub().returns(Promise.resolve())
- }
+ write: sinon.stub().returns(Promise.resolve()),
+ },
});
await duplexCollection._write(resource);
@@ -164,10 +164,10 @@ test("DuplexCollection: Throws for empty reader", (t) => {
t.throws(() => {
new DuplexCollection({
name: "myReader",
- writer: {}
+ writer: {},
});
}, {
- message: "Cannot create DuplexCollection myReader: No reader provided"
+ message: "Cannot create DuplexCollection myReader: No reader provided",
});
});
@@ -175,9 +175,9 @@ test("DuplexCollection: Throws for empty writer", (t) => {
t.throws(() => {
new DuplexCollection({
name: "myReader",
- reader: {}
+ reader: {},
});
}, {
- message: "Cannot create DuplexCollection myReader: No writer provided"
+ message: "Cannot create DuplexCollection myReader: No writer provided",
});
});
diff --git a/test/lib/ReaderCollection.js b/test/lib/ReaderCollection.ts
similarity index 81%
rename from test/lib/ReaderCollection.js
rename to test/lib/ReaderCollection.ts
index d808591b..63949291 100644
--- a/test/lib/ReaderCollection.js
+++ b/test/lib/ReaderCollection.ts
@@ -1,12 +1,12 @@
import test from "ava";
import sinon from "sinon";
-import ReaderCollection from "../../lib/ReaderCollection.js";
-import Resource from "../../lib/Resource.js";
+import ReaderCollection from "../../src/ReaderCollection.js";
+import Resource from "../../src/Resource.js";
test("ReaderCollection: constructor", (t) => {
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [{}, {}, {}]
+ readers: [{}, {}, {}],
});
t.is(readerCollection.getName(), "myReader", "correct name assigned");
@@ -17,14 +17,14 @@ test("ReaderCollection: _byGlob w/o finding a resource", async (t) => {
t.plan(4);
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([]))
+ _byGlob: sinon.stub().returns(Promise.resolve([])),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const resources = await readerCollection._byGlob("anyPattern", {someOption: true}, trace);
@@ -40,17 +40,17 @@ test("ReaderCollection: _byGlob with finding a resource", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([resource]))
+ _byGlob: sinon.stub().returns(Promise.resolve([resource])),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const resources = await readerCollection._byGlob("anyPattern", {someOption: true}, trace);
@@ -70,18 +70,18 @@ test("ReaderCollection: _byPath with reader finding a resource", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const pushCollectionSpy = sinon.spy(resource, "pushCollection");
const abstractReader = {
- _byPath: sinon.stub().returns(Promise.resolve(resource))
+ _byPath: sinon.stub().returns(Promise.resolve(resource)),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const readResource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -99,17 +99,17 @@ test("ReaderCollection: _byPath with two readers both finding no resource", asyn
t.plan(4);
const abstractReaderOne = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const abstractReaderTwo = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReaderOne, abstractReaderTwo]
+ readers: [abstractReaderOne, abstractReaderTwo],
});
const resource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -124,11 +124,11 @@ test("ReaderCollection: _byPath with two readers both finding no resource", asyn
test("ReaderCollection: _byPath with empty readers array", async (t) => {
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: []
+ readers: [],
});
const resource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -138,21 +138,21 @@ test("ReaderCollection: _byPath with empty readers array", async (t) => {
test("ReaderCollection: _byPath with some empty readers", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReaderOne = {
- _byPath: sinon.stub().resolves(resource)
+ _byPath: sinon.stub().resolves(resource),
};
const abstractReaderTwo = {
- _byPath: sinon.stub().resolves()
+ _byPath: sinon.stub().resolves(),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReaderOne, undefined, abstractReaderTwo]
+ readers: [abstractReaderOne, undefined, abstractReaderTwo],
});
const res = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -161,11 +161,11 @@ test("ReaderCollection: _byPath with some empty readers", async (t) => {
test("ReaderCollection: _byGlob with empty readers array", async (t) => {
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: []
+ readers: [],
});
const resource = await readerCollection.byGlob("anyPattern", {someOption: true}, trace);
@@ -175,21 +175,21 @@ test("ReaderCollection: _byGlob with empty readers array", async (t) => {
test("ReaderCollection: _byGlob with some empty readers", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReaderOne = {
- _byGlob: sinon.stub().resolves([resource])
+ _byGlob: sinon.stub().resolves([resource]),
};
const abstractReaderTwo = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new ReaderCollection({
name: "myReader",
- readers: [abstractReaderOne, undefined, abstractReaderTwo]
+ readers: [abstractReaderOne, undefined, abstractReaderTwo],
});
const res = await readerCollection._byGlob("anyVirtualPath", {someOption: true}, trace);
diff --git a/test/lib/ReaderCollectionPrioritized.js b/test/lib/ReaderCollectionPrioritized.ts
similarity index 81%
rename from test/lib/ReaderCollectionPrioritized.js
rename to test/lib/ReaderCollectionPrioritized.ts
index e13cda12..fe6a4444 100644
--- a/test/lib/ReaderCollectionPrioritized.js
+++ b/test/lib/ReaderCollectionPrioritized.ts
@@ -1,12 +1,12 @@
import test from "ava";
import sinon from "sinon";
-import ReaderCollectionPrioritized from "../../lib/ReaderCollectionPrioritized.js";
-import Resource from "../../lib/Resource.js";
+import ReaderCollectionPrioritized from "../../src/ReaderCollectionPrioritized.js";
+import Resource from "../../src/Resource.js";
test("ReaderCollectionPrioritized: constructor", (t) => {
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [{}, {}, {}]
+ readers: [{}, {}, {}],
});
t.is(readerCollectionPrioritized.getName(), "myReader", "correct name assigned");
@@ -15,14 +15,14 @@ test("ReaderCollectionPrioritized: constructor", (t) => {
test("ReaderCollectionPrioritized: _byGlob w/o finding a resource", async (t) => {
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([]))
+ _byGlob: sinon.stub().returns(Promise.resolve([])),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const resources = await readerCollectionPrioritized._byGlob("anyPattern", {someOption: true}, trace);
@@ -36,17 +36,17 @@ test("ReaderCollectionPrioritized: _byGlob w/o finding a resource", async (t) =>
test("ReaderCollectionPrioritized: _byGlob with finding a resource", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve([resource]))
+ _byGlob: sinon.stub().returns(Promise.resolve([resource])),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const resources = await readerCollectionPrioritized._byGlob("anyPattern", {someOption: true}, trace);
@@ -64,18 +64,18 @@ test("ReaderCollectionPrioritized: _byGlob with finding a resource", async (t) =
test("ReaderCollectionPrioritized: _byPath with reader finding a resource", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const pushCollectionSpy = sinon.spy(resource, "pushCollection");
const abstractReader = {
- _byPath: sinon.stub().returns(Promise.resolve(resource))
+ _byPath: sinon.stub().returns(Promise.resolve(resource)),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReader]
+ readers: [abstractReader],
});
const readResource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -90,17 +90,17 @@ test("ReaderCollectionPrioritized: _byPath with reader finding a resource", asyn
test("ReaderCollectionPrioritized: _byPath with two readers both finding no resource", async (t) => {
const abstractReaderOne = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const abstractReaderTwo = {
- _byPath: sinon.stub().returns(Promise.resolve())
+ _byPath: sinon.stub().returns(Promise.resolve()),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReaderOne, abstractReaderTwo]
+ readers: [abstractReaderOne, abstractReaderTwo],
});
const resource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -114,11 +114,11 @@ test("ReaderCollectionPrioritized: _byPath with two readers both finding no reso
test("ReaderCollectionPrioritized: _byPath with empty readers array", async (t) => {
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: []
+ readers: [],
});
const resource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -128,21 +128,21 @@ test("ReaderCollectionPrioritized: _byPath with empty readers array", async (t)
test("ReaderCollectionPrioritized: _byPath with some empty readers", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReaderOne = {
- _byPath: sinon.stub().resolves(resource)
+ _byPath: sinon.stub().resolves(resource),
};
const abstractReaderTwo = {
- _byPath: sinon.stub().resolves()
+ _byPath: sinon.stub().resolves(),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReaderOne, undefined, abstractReaderTwo]
+ readers: [abstractReaderOne, undefined, abstractReaderTwo],
});
const res = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace);
@@ -151,11 +151,11 @@ test("ReaderCollectionPrioritized: _byPath with some empty readers", async (t) =
test("ReaderCollectionPrioritized: _byGlob with empty readers array", async (t) => {
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: []
+ readers: [],
});
const resource = await readerCollectionPrioritized.byGlob("anyPattern", {someOption: true}, trace);
@@ -165,21 +165,21 @@ test("ReaderCollectionPrioritized: _byGlob with empty readers array", async (t)
test("ReaderCollectionPrioritized: _byGlob with some empty readers", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("content")
+ buffer: Buffer.from("content"),
});
const abstractReaderOne = {
- _byGlob: sinon.stub().resolves([resource])
+ _byGlob: sinon.stub().resolves([resource]),
};
const abstractReaderTwo = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollectionPrioritized = new ReaderCollectionPrioritized({
name: "myReader",
- readers: [abstractReaderOne, undefined, abstractReaderTwo]
+ readers: [abstractReaderOne, undefined, abstractReaderTwo],
});
const res = await readerCollectionPrioritized._byGlob("anyVirtualPath", {someOption: true}, trace);
diff --git a/test/lib/Resource.js b/test/lib/Resource.ts
similarity index 86%
rename from test/lib/Resource.js
rename to test/lib/Resource.ts
index aca04728..7c560bc7 100644
--- a/test/lib/Resource.js
+++ b/test/lib/Resource.ts
@@ -1,19 +1,19 @@
import test from "ava";
-import {Stream, Transform} from "node:stream";
+import {Stream, Transform, type Readable} from "node:stream";
import {promises as fs, createReadStream} from "node:fs";
import path from "node:path";
-import Resource from "../../lib/Resource.js";
+import Resource from "../../src/Resource.js";
+import type {Project} from "@ui5/project/specifications/Project";
function createBasicResource() {
const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html");
const resource = new Resource({
path: "/app/index.html",
- createStream: function() {
+ createStream: function () {
return createReadStream(fsPath);
},
- project: {},
+ project: {} as Project,
statInfo: {},
- fsPath
});
return resource;
}
@@ -24,16 +24,16 @@ function createBasicResource() {
* @param {stream.Readable} readableStream readable stream
* @returns {Promise} resolves with the read string
*/
-const readStream = (readableStream) => {
+const readStream = (readableStream: Readable) => {
return new Promise((resolve, reject) => {
let streamedResult = "";
- readableStream.on("data", (chunk) => {
+ readableStream.on("data", (chunk: string) => {
streamedResult += chunk;
});
readableStream.on("end", () => {
resolve(streamedResult);
});
- readableStream.on("error", (err) => {
+ readableStream.on("error", (err: Error) => {
reject(err);
});
});
@@ -41,10 +41,11 @@ const readStream = (readableStream) => {
test("Resource: constructor with missing path parameter", (t) => {
t.throws(() => {
+ // @ts-expect-error testing missing arguments
new Resource({});
}, {
instanceOf: Error,
- message: "Unable to create Resource: Missing parameter 'path'"
+ message: "Unable to create Resource: Missing parameter 'path'",
});
});
@@ -86,7 +87,7 @@ test("Resource: constructor with duplicated content parameter", (t) => {
}, {
instanceOf: Error,
message: "Unable to create Resource: Please set only one content parameter. " +
- "'buffer', 'string', 'stream' or 'createStream'"
+ "'buffer', 'string', 'stream' or 'createStream'",
}, "Threw with expected error message");
});
});
@@ -94,7 +95,7 @@ test("Resource: constructor with duplicated content parameter", (t) => {
test("Resource: From buffer", async (t) => {
const resource = new Resource({
path: "/my/path",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
t.is(await resource.getSize(), 7, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -104,7 +105,7 @@ test("Resource: From buffer", async (t) => {
test("Resource: From string", async (t) => {
const resource = new Resource({
path: "/my/path",
- string: "Content"
+ string: "Content",
});
t.is(await resource.getSize(), 7, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -115,7 +116,7 @@ test("Resource: From stream", async (t) => {
const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html");
const resource = new Resource({
path: "/my/path",
- stream: createReadStream(fsPath)
+ stream: createReadStream(fsPath),
});
t.is(await resource.getSize(), 91, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -128,7 +129,7 @@ test("Resource: From createStream", async (t) => {
path: "/my/path",
createStream: () => {
return createReadStream(fsPath);
- }
+ },
});
t.is(await resource.getSize(), 91, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -141,8 +142,8 @@ test("Resource: Source metadata", async (t) => {
string: "Content",
sourceMetadata: {
adapter: "My Adapter",
- fsPath: "/some/path"
- }
+ fsPath: "/some/path",
+ },
});
t.is(await resource.getSize(), 7, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -157,8 +158,8 @@ test("Resource: Source metadata with modified content", async (t) => {
sourceMetadata: {
adapter: "My Adapter",
fsPath: "/some/path",
- contentModified: true
- }
+ contentModified: true,
+ },
});
t.is(await resource.getSize(), 7, "Content is set");
t.false(resource.isModified(), "Content of new resource is not modified");
@@ -175,11 +176,12 @@ test("Resource: Illegal source metadata attribute", (t) => {
sourceMetadata: {
adapter: "My Adapter",
fsPath: "/some/path",
- pony: "🦄"
- }
+ // @ts-expect-error testing invalid value
+ pony: "🦄",
+ },
});
}, {
- message: `Parameter 'sourceMetadata' contains an illegal attribute: pony`
+ message: `Parameter 'sourceMetadata' contains an illegal attribute: pony`,
}, "Threw with expected error message");
});
@@ -188,15 +190,16 @@ test("Resource: Illegal source metadata value", (t) => {
new Resource({
path: "/my/path",
string: "Content",
+ // @ts-expect-error testing invalid value
sourceMetadata: {
adapter: "My Adapter",
fsPath: {
- some: "value"
- }
- }
+ some: "value",
+ },
+ },
});
}, {
- message: `Attribute 'fsPath' of parameter 'sourceMetadata' must be of type "string" or "boolean"`
+ message: `Attribute 'fsPath' of parameter 'sourceMetadata' must be of type "string" or "boolean"`,
}, "Threw with expected error message");
});
@@ -207,7 +210,7 @@ test("Resource: getBuffer with throwing an error", (t) => {
path: "/my/path/to/resource",
});
- return resource.getBuffer().catch(function(error) {
+ return resource.getBuffer().catch(function (error) {
t.is(error.message, "Resource /my/path/to/resource has no content",
"getBuffer called w/o having a resource content provided");
});
@@ -216,7 +219,7 @@ test("Resource: getBuffer with throwing an error", (t) => {
test("Resource: getPath / getName", (t) => {
const resource = new Resource({
path: "/my/path/to/resource.js",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
t.is(resource.getPath(), "/my/path/to/resource.js", "Correct path");
t.is(resource.getName(), "resource.js", "Correct name");
@@ -225,7 +228,7 @@ test("Resource: getPath / getName", (t) => {
test("Resource: setPath / getName", (t) => {
const resource = new Resource({
path: "/my/path/to/resource.js",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
resource.setPath("/my/other/file.json");
t.is(resource.getPath(), "/my/other/file.json", "Correct path");
@@ -235,12 +238,12 @@ test("Resource: setPath / getName", (t) => {
test("Resource: setPath with non-absolute path", (t) => {
const resource = new Resource({
path: "/my/path/to/resource.js",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
t.throws(() => {
resource.setPath("my/other/file.json");
}, {
- message: "Unable to set resource path: Path must be absolute: my/other/file.json"
+ message: "Unable to set resource path: Path must be absolute: my/other/file.json",
}, "Threw with expected error message");
t.is(resource.getPath(), "/my/path/to/resource.js", "Path is unchanged");
t.is(resource.getName(), "resource.js", "Name is unchanged");
@@ -250,10 +253,10 @@ test("Create Resource with non-absolute path", (t) => {
t.throws(() => {
new Resource({
path: "my/path/to/resource.js",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
}, {
- message: "Unable to set resource path: Path must be absolute: my/path/to/resource.js"
+ message: "Unable to set resource path: Path must be absolute: my/path/to/resource.js",
}, "Threw with expected error message");
});
@@ -262,7 +265,7 @@ test("Resource: getStream", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
const result = await readStream(resource.getStream());
@@ -274,7 +277,7 @@ test("Resource: getStream for empty string", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- string: ""
+ string: "",
});
const result = await readStream(resource.getStream());
@@ -286,8 +289,8 @@ test("Resource: getStream for empty string instance", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- // eslint-disable-next-line no-new-wrappers
- string: new String("")
+
+ string: new String("") as string,
});
const result = await readStream(resource.getStream());
@@ -296,21 +299,21 @@ test("Resource: getStream for empty string instance", async (t) => {
test("Resource: getStream throwing an error", (t) => {
const resource = new Resource({
- path: "/my/path/to/resource"
+ path: "/my/path/to/resource",
});
t.throws(() => {
resource.getStream();
}, {
instanceOf: Error,
- message: "Resource /my/path/to/resource has no content"
+ message: "Resource /my/path/to/resource has no content",
});
});
test("Resource: setString", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- sourceMetadata: {} // Needs to be passed in order to get the "modified" state
+ sourceMetadata: {}, // Needs to be passed in order to get the "modified" state
});
t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly");
@@ -328,7 +331,7 @@ test("Resource: setString", async (t) => {
test("Resource: setBuffer", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- sourceMetadata: {} // Needs to be passed in order to get the "modified" state
+ sourceMetadata: {}, // Needs to be passed in order to get the "modified" state
});
t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly");
@@ -345,7 +348,7 @@ test("Resource: setBuffer", async (t) => {
test("Resource: size modification", async (t) => {
const resource = new Resource({
- path: "/my/path/to/resource"
+ path: "/my/path/to/resource",
});
t.is(await resource.getSize(), 0, "initial size without content");
@@ -355,7 +358,7 @@ test("Resource: size modification", async (t) => {
t.is(await resource.getSize(), 7, "size after manually setting the string");
t.is(await new Resource({
path: "/my/path/to/resource",
- string: "Content"
+ string: "Content",
}).getSize(), 7, "size when passing string to constructor");
// buffer
@@ -374,7 +377,7 @@ test("Resource: size modification", async (t) => {
t.is(await resource.getSize(), 1234, "buffer with alloc after setting the buffer");
t.is(await new Resource({
path: "/my/path/to/resource",
- buffer: buf
+ buffer: buf,
}).getSize(), 1234, "buffer with alloc when passing buffer to constructor");
const clonedResource2 = await resource.clone();
@@ -385,7 +388,8 @@ test("Resource: size modification", async (t) => {
path: "/my/path/to/resource",
});
const stream = new Stream.Readable();
- stream._read = function() {};
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ stream._read = function () {};
stream.push("I am a ");
stream.push("readable ");
stream.push("stream!");
@@ -404,14 +408,15 @@ test("Resource: size modification", async (t) => {
test("Resource: setStream (Stream)", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- sourceMetadata: {} // Needs to be passed in order to get the "modified" state
+ sourceMetadata: {}, // Needs to be passed in order to get the "modified" state
});
t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly");
t.false(resource.isModified(), "Resource is not modified");
const stream = new Stream.Readable();
- stream._read = function() {};
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ stream._read = function () {};
stream.push("I am a ");
stream.push("readable ");
stream.push("stream!");
@@ -429,7 +434,7 @@ test("Resource: setStream (Stream)", async (t) => {
test("Resource: setStream (Create stream callback)", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- sourceMetadata: {} // Needs to be passed in order to get the "modified" state
+ sourceMetadata: {}, // Needs to be passed in order to get the "modified" state
});
t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly");
@@ -437,7 +442,8 @@ test("Resource: setStream (Create stream callback)", async (t) => {
resource.setStream(() => {
const stream = new Stream.Readable();
- stream._read = function() {};
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ stream._read = function () {};
stream.push("I am a ");
stream.push("readable ");
stream.push("stream!");
@@ -457,7 +463,7 @@ test("Resource: clone resource with buffer", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- buffer: Buffer.from("Content")
+ buffer: Buffer.from("Content"),
});
const clonedResource = await resource.clone();
@@ -471,10 +477,11 @@ test("Resource: clone resource with stream", async (t) => {
t.plan(2);
const resource = new Resource({
- path: "/my/path/to/resource"
+ path: "/my/path/to/resource",
});
const stream = new Stream.Readable();
- stream._read = function() {};
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ stream._read = function () {};
stream.push("Content");
stream.push(null);
@@ -492,8 +499,8 @@ test("Resource: clone resource with sourceMetadata", async (t) => {
path: "/my/path/to/resource",
sourceMetadata: {
adapter: "FileSystem",
- fsPath: "/resources/my.js"
- }
+ fsPath: "/resources/my.js",
+ },
});
const clonedResource = await resource.clone();
@@ -526,18 +533,18 @@ test("Resource: clone resource with sourceMetadata", async (t) => {
test("Resource: clone resource with project removes project", async (t) => {
const myProject = {
- name: "my project"
+ name: "my project",
};
const resource = new Resource({
path: "/my/path/to/resource",
- project: myProject
+ project: myProject as unknown as Project,
});
const clonedResource = await resource.clone();
t.pass("Resource cloned");
- const clonedResourceProject = await clonedResource.getProject();
+ const clonedResourceProject = clonedResource.getProject();
t.falsy(clonedResourceProject, "Cloned resource should not have a project");
});
@@ -547,8 +554,8 @@ test("Resource: create resource with sourceMetadata.contentModified: true", (t)
sourceMetadata: {
adapter: "FileSystem",
fsPath: "/resources/my.js",
- contentModified: true
- }
+ contentModified: true,
+ },
});
t.true(resource.getSourceMetadata().contentModified, "Modified flag is still true");
@@ -556,7 +563,7 @@ test("Resource: create resource with sourceMetadata.contentModified: true", (t)
});
test("getStream with createStream callback content: Subsequent content requests should throw error due " +
- "to drained content", async (t) => {
+"to drained content", async (t) => {
const resource = createBasicResource();
resource.getStream();
t.throws(() => {
@@ -567,7 +574,7 @@ test("getStream with createStream callback content: Subsequent content requests
});
test("getStream with Buffer content: Subsequent content requests should throw error due to drained " +
- "content", async (t) => {
+"content", async (t) => {
const resource = createBasicResource();
await resource.getBuffer();
resource.getStream();
@@ -579,13 +586,13 @@ test("getStream with Buffer content: Subsequent content requests should throw er
});
test("getStream with Stream content: Subsequent content requests should throw error due to drained " +
- "content", async (t) => {
+"content", async (t) => {
const resource = createBasicResource();
const tStream = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString());
callback();
- }
+ },
});
const stream = resource.getStream();
stream.pipe(tStream);
@@ -600,13 +607,13 @@ test("getStream with Stream content: Subsequent content requests should throw er
});
test("getBuffer from Stream content: Subsequent content requests should not throw error due to drained " +
- "content", async (t) => {
+"content", async (t) => {
const resource = createBasicResource();
const tStream = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString());
callback();
- }
+ },
});
const stream = resource.getStream();
stream.pipe(tStream);
@@ -626,37 +633,38 @@ test("Resource: getProject", (t) => {
t.plan(1);
const resource = new Resource({
path: "/my/path/to/resource",
- project: {getName: () => "Mock Project"}
+ project: {getName: () => "Mock Project"} as Project,
});
const project = resource.getProject();
- t.is(project.getName(), "Mock Project");
+ t.is(project!.getName(), "Mock Project");
});
test("Resource: setProject", (t) => {
t.plan(1);
const resource = new Resource({
- path: "/my/path/to/resource"
+ path: "/my/path/to/resource",
});
- const project = {getName: () => "Mock Project"};
+ const project = {getName: () => "Mock Project"} as Project;
resource.setProject(project);
- t.is(resource.getProject().getName(), "Mock Project");
+ t.is(resource.getProject()!.getName(), "Mock Project");
});
test("Resource: reassign with setProject", (t) => {
t.plan(2);
const resource = new Resource({
path: "/my/path/to/resource",
- project: {getName: () => "Mock Project"}
+ project: {getName: () => "Mock Project"} as Project,
});
- const project = {getName: () => "New Mock Project"};
+ const project = {getName: () => "New Mock Project"} as Project;
const error = t.throws(() => resource.setProject(project));
t.is(error.message, "Unable to assign project New Mock Project to resource /my/path/to/resource: " +
- "Resource is already associated to project " + project);
+ "Resource is already associated to project Mock Project");
});
test("Resource: constructor with stream", async (t) => {
const stream = new Stream.Readable();
- stream._read = function() {};
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ stream._read = function () {};
stream.push("I am a ");
stream.push("readable ");
stream.push("stream!");
@@ -665,7 +673,7 @@ test("Resource: constructor with stream", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
stream,
- sourceMetadata: {} // Needs to be passed in order to get the "modified" state
+ sourceMetadata: {}, // Needs to be passed in order to get the "modified" state
});
t.is(resource.getSourceMetadata().contentModified, false);
@@ -686,7 +694,7 @@ test("integration stat - resource size", async (t) => {
statInfo,
createStream: () => {
return createReadStream(fsPath);
- }
+ },
});
t.is(await resource.getSize(), 91);
diff --git a/test/lib/ResourceFacade.js b/test/lib/ResourceFacade.ts
similarity index 75%
rename from test/lib/ResourceFacade.js
rename to test/lib/ResourceFacade.ts
index 5dee2fc8..ebf4e274 100644
--- a/test/lib/ResourceFacade.js
+++ b/test/lib/ResourceFacade.ts
@@ -1,20 +1,20 @@
import test from "ava";
import sinon from "sinon";
-import Resource from "../../lib/Resource.js";
-import ResourceFacade from "../../lib/ResourceFacade.js";
+import Resource, {type ResourceInterface} from "../../src/Resource.js";
+import ResourceFacade from "../../src/ResourceFacade.js";
-test.afterEach.always( (t) => {
+test.afterEach.always(() => {
sinon.restore();
});
test("Create instance", (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- string: "my content"
+ string: "my content",
});
const resourceFacade = new ResourceFacade({
path: "/my/path",
- resource
+ resource,
});
t.is(resourceFacade.getPath(), "/my/path", "Returns correct path");
t.is(resourceFacade.getName(), "path", "Returns correct name");
@@ -23,31 +23,33 @@ test("Create instance", (t) => {
test("Create instance: Missing parameters", (t) => {
t.throws(() => {
+ // @ts-expect-error testing missing arguments
new ResourceFacade({
path: "/my/path",
});
}, {
instanceOf: Error,
- message: "Unable to create ResourceFacade: Missing parameter 'resource'"
+ message: "Unable to create ResourceFacade: Missing parameter 'resource'",
});
t.throws(() => {
new ResourceFacade({
+ // @ts-expect-error testing missing arguments
resource: {},
});
}, {
instanceOf: Error,
- message: "Unable to create ResourceFacade: Missing parameter 'path'"
+ message: "Unable to create ResourceFacade: Missing parameter 'path'",
});
});
test("ResourceFacade #clone", async (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- string: "my content"
+ string: "my content",
});
const resourceFacade = new ResourceFacade({
path: "/my/path",
- resource
+ resource,
});
const clone = await resourceFacade.clone();
@@ -58,11 +60,11 @@ test("ResourceFacade #clone", async (t) => {
test("ResourceFacade #setPath", (t) => {
const resource = new Resource({
path: "/my/path/to/resource",
- string: "my content"
+ string: "my content",
});
const resourceFacade = new ResourceFacade({
path: "/my/path",
- resource
+ resource,
});
const err = t.throws(() => {
@@ -72,25 +74,27 @@ test("ResourceFacade #setPath", (t) => {
});
test("ResourceFacade provides same public functions as Resource", (t) => {
- const resource = new Resource({
+ const resource: ResourceInterface = new Resource({
path: "/my/path/to/resource",
- string: "my content"
+ string: "my content",
});
const resourceFacade = new ResourceFacade({
path: "/my/path",
- resource
+ resource,
});
const methods = Object.getOwnPropertyNames(Resource.prototype)
- .filter((p) => (!p.startsWith("_") && typeof resource[p] === "function"));
+ .filter((p) => (!p.startsWith("_") && typeof resource[p as keyof typeof resource] === "function"));
methods.forEach((method) => {
- t.truthy(resourceFacade[method], `resourceFacade provides function #${method}`);
+ t.truthy(method in resourceFacade, `resourceFacade provides function #${method}`);
if (["constructor", "getPath", "getName", "setPath", "clone"].includes(method)) {
// special functions with separate tests
return;
}
+ // @ts-expect-error Stubbing the resource
const stub = sinon.stub(resource, method);
+ // @ts-expect-error Checking stubbed resource
resourceFacade[method]("argument");
t.is(stub.callCount, 1, `Resource#${method} stub got called once by resourceFacade#${method}`);
stub.restore();
diff --git a/test/lib/ResourceTagCollection.js b/test/lib/ResourceTagCollection.ts
similarity index 82%
rename from test/lib/ResourceTagCollection.js
rename to test/lib/ResourceTagCollection.ts
index 1329187c..cf1d8093 100644
--- a/test/lib/ResourceTagCollection.js
+++ b/test/lib/ResourceTagCollection.ts
@@ -1,18 +1,18 @@
import test from "ava";
import sinon from "sinon";
-import Resource from "../../lib/Resource.js";
-import ResourceTagCollection from "../../lib/ResourceTagCollection.js";
+import Resource from "../../src/Resource.js";
+import ResourceTagCollection from "../../src/ResourceTagCollection.js";
-test.afterEach.always((t) => {
+test.afterEach.always(() => {
sinon.restore();
});
test("setTag", (t) => {
const resource = new Resource({
- path: "/some/path"
+ path: "/some/path",
});
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
const validateResourceSpy = sinon.spy(tagCollection, "_getPath");
@@ -23,8 +23,8 @@ test("setTag", (t) => {
t.deepEqual(tagCollection._pathTags, {
"/some/path": {
- "abc:MyTag": "my value"
- }
+ "abc:MyTag": "my value",
+ },
}, "Tag correctly stored");
t.is(validateResourceSpy.callCount, 1, "_getPath called once");
@@ -42,26 +42,26 @@ test("setTag", (t) => {
test("setTag: Value defaults to true", (t) => {
const resource = new Resource({
- path: "/some/path"
+ path: "/some/path",
});
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
tagCollection.setTag(resource, "abc:MyTag");
t.deepEqual(tagCollection._pathTags, {
"/some/path": {
- "abc:MyTag": true
- }
+ "abc:MyTag": true,
+ },
}, "Tag correctly stored");
});
test("getTag", (t) => {
const resource = new Resource({
- path: "/some/path"
+ path: "/some/path",
});
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
tagCollection.setTag(resource, "abc:MyTag", 123);
@@ -83,15 +83,15 @@ test("getTag", (t) => {
test("getTag with prefilled tags", (t) => {
const resource = new Resource({
- path: "/some/path"
+ path: "/some/path",
});
const tagCollection = new ResourceTagCollection({
allowedTags: ["abc:MyTag"],
tags: {
"/some/path": {
- "abc:MyTag": 123
- }
- }
+ "abc:MyTag": 123,
+ },
+ },
});
const validateResourceSpy = sinon.spy(tagCollection, "_getPath");
@@ -112,10 +112,10 @@ test("getTag with prefilled tags", (t) => {
test("clearTag", (t) => {
const resource = new Resource({
- path: "/some/path"
+ path: "/some/path",
});
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
tagCollection.setTag(resource, "abc:MyTag", 123);
@@ -127,8 +127,8 @@ test("clearTag", (t) => {
t.deepEqual(tagCollection._pathTags, {
"/some/path": {
- "abc:MyTag": undefined
- }
+ "abc:MyTag": undefined,
+ },
}, "Tag value set to undefined");
t.is(validateResourceSpy.callCount, 1, "_getPath called once");
@@ -142,130 +142,130 @@ test("clearTag", (t) => {
test("_validateTag: Not in list of allowed tags", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
t.throws(() => {
tagCollection._validateTag("abc:MyOtherTag");
}, {
instanceOf: Error,
message: `Tag "abc:MyOtherTag" not accepted by this collection. ` +
- `Accepted tags are: abc:MyTag. Accepted namespaces are: *none*`
+ `Accepted tags are: abc:MyTag. Accepted namespaces are: *none*`,
});
});
test("_validateTag: Empty list of tags and namespaces", (t) => {
const tagCollection = new ResourceTagCollection({
allowedTags: [],
- allowedNamespaces: []
+ allowedNamespaces: [],
});
t.throws(() => {
tagCollection._validateTag("abc:MyOtherTag");
}, {
instanceOf: Error,
message: `Tag "abc:MyOtherTag" not accepted by this collection. ` +
- `Accepted tags are: *none*. Accepted namespaces are: *none*`
+ `Accepted tags are: *none*. Accepted namespaces are: *none*`,
});
});
test("_validateTag: Missing colon", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["aBcMyTag"]
+ allowedTags: ["aBcMyTag"],
});
t.throws(() => {
tagCollection._validateTag("aBcMyTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "aBcMyTag": Colon required after namespace`
+ message: `Invalid Tag "aBcMyTag": Colon required after namespace`,
});
});
test("_validateTag: Too many colons", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["aBc:My:Tag"]
+ allowedTags: ["aBc:My:Tag"],
});
t.throws(() => {
tagCollection._validateTag("aBc:My:Tag");
}, {
instanceOf: Error,
- message: `Invalid Tag "aBc:My:Tag": Expected exactly one colon but found 2`
+ message: `Invalid Tag "aBc:My:Tag": Expected exactly one colon but found 2`,
});
});
test("_validateTag: Invalid namespace with uppercase letter", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["aBc:MyTag"]
+ allowedTags: ["aBc:MyTag"],
});
t.throws(() => {
tagCollection._validateTag("aBc:MyTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "aBc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`
+ message: `Invalid Tag "aBc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`,
});
});
test("_validateTag: Invalid namespace starting with number", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["0abc:MyTag"]
+ allowedTags: ["0abc:MyTag"],
});
t.throws(() => {
tagCollection._validateTag("0abc:MyTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "0abc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`
+ message: `Invalid Tag "0abc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`,
});
});
test("_validateTag: Invalid namespace containing an illegal character", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["a🦦c:MyTag"]
+ allowedTags: ["a🦦c:MyTag"],
});
t.throws(() => {
tagCollection._validateTag("a🦦c:MyTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "a🦦c:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`
+ message: `Invalid Tag "a🦦c:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter`,
});
});
test("_validateTag: Invalid tag name starting with number", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:0MyTag"]
+ allowedTags: ["abc:0MyTag"],
});
t.throws(() => {
tagCollection._validateTag("abc:0MyTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "abc:0MyTag": Name part must be alphanumeric and start with a capital letter`
+ message: `Invalid Tag "abc:0MyTag": Name part must be alphanumeric and start with a capital letter`,
});
});
test("_validateTag: Invalid tag name starting with lowercase letter", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:myTag"]
+ allowedTags: ["abc:myTag"],
});
t.throws(() => {
tagCollection._validateTag("abc:myTag");
}, {
instanceOf: Error,
- message: `Invalid Tag "abc:myTag": Name part must be alphanumeric and start with a capital letter`
+ message: `Invalid Tag "abc:myTag": Name part must be alphanumeric and start with a capital letter`,
});
});
test("_validateTag: Invalid tag name containing an illegal character", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:My/Tag"]
+ allowedTags: ["abc:My/Tag"],
});
t.throws(() => {
tagCollection._validateTag("abc:My/Tag");
}, {
instanceOf: Error,
- message: `Invalid Tag "abc:My/Tag": Name part must be alphanumeric and start with a capital letter`
+ message: `Invalid Tag "abc:My/Tag": Name part must be alphanumeric and start with a capital letter`,
});
});
test("_validateValue: Valid values", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
tagCollection._validateValue("bla");
tagCollection._validateValue("");
@@ -279,50 +279,54 @@ test("_validateValue: Valid values", (t) => {
test("_validateValue: Invalid value of type object", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
t.throws(() => {
+ // @ts-expect-error testing invalid value
tagCollection._validateValue({foo: "bar"});
}, {
instanceOf: Error,
- message: "Invalid Tag Value: Must be of type string, number or boolean but is object"
+ message: "Invalid Tag Value: Must be of type string, number or boolean but is object",
});
});
test("_validateValue: Invalid value undefined", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
t.throws(() => {
+ // @ts-expect-error testing invalid value
tagCollection._validateValue(undefined);
}, {
instanceOf: Error,
- message: "Invalid Tag Value: Must be of type string, number or boolean but is undefined"
+ message: "Invalid Tag Value: Must be of type string, number or boolean but is undefined",
});
});
test("_validateValue: Invalid value null", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
t.throws(() => {
+ // @ts-expect-error testing invalid value
tagCollection._validateValue(null);
}, {
instanceOf: Error,
- message: "Invalid Tag Value: Must be of type string, number or boolean but is object"
+ message: "Invalid Tag Value: Must be of type string, number or boolean but is object",
});
});
test("_getPath: Empty path", (t) => {
const tagCollection = new ResourceTagCollection({
- allowedTags: ["abc:MyTag"]
+ allowedTags: ["abc:MyTag"],
});
t.throws(() => {
+ // @ts-expect-error testing invalid value
tagCollection._getPath({
- getPath: () => ""
+ getPath: () => "",
});
}, {
instanceOf: Error,
- message: "Invalid Resource: Resource path must not be empty"
+ message: "Invalid Resource: Resource path must not be empty",
});
});
diff --git a/test/lib/WriterCollection.js b/test/lib/WriterCollection.ts
similarity index 87%
rename from test/lib/WriterCollection.js
rename to test/lib/WriterCollection.ts
index 7e389452..ee6b50fe 100644
--- a/test/lib/WriterCollection.js
+++ b/test/lib/WriterCollection.ts
@@ -1,7 +1,7 @@
import test from "ava";
import sinon from "sinon";
-import WriterCollection from "../../lib/WriterCollection.js";
-import Resource from "../../lib/Resource.js";
+import WriterCollection from "../../src/WriterCollection.js";
+import Resource from "../../src/Resource.js";
test("Constructor: Path mapping regex", (t) => {
const myWriter = {};
@@ -11,7 +11,7 @@ test("Constructor: Path mapping regex", (t) => {
"/": myWriter,
"/my/path/": myWriter,
"/my/": myWriter,
- }
+ },
});
t.is(writer._basePathRegex.toString(), "^((?:/)??(?:/my/)??(?:/my/path/)??)+.*?$",
"Created correct path mapping regular expression");
@@ -25,7 +25,7 @@ test("Constructor: Path mapping regex has correct escaping", (t) => {
"/My\\Weird.Path/": myWriter,
"/my/pa$h/": myWriter,
"/my/": myWriter,
- }
+ },
});
t.is(writer._basePathRegex.toString(), "^((?:/My\\\\Weird\\.Path/)??(?:/my/)??(?:/my/pa\\$h/)??)+.*?$",
"Created correct path mapping regular expression");
@@ -34,7 +34,7 @@ test("Constructor: Path mapping regex has correct escaping", (t) => {
test("Constructor: Throws for missing path mapping", (t) => {
const err = t.throws(() => {
new WriterCollection({
- name: "myCollection"
+ name: "myCollection",
});
});
t.is(err.message, "Cannot create WriterCollection myCollection: Missing parameter 'writerMapping'",
@@ -45,7 +45,7 @@ test("Constructor: Throws for empty path mapping", (t) => {
const err = t.throws(() => {
new WriterCollection({
name: "myCollection",
- writerMapping: {}
+ writerMapping: {},
});
});
t.is(err.message, "Cannot create WriterCollection myCollection: Empty parameter 'writerMapping'",
@@ -54,14 +54,14 @@ test("Constructor: Throws for empty path mapping", (t) => {
test("Constructor: Throws for empty path", (t) => {
const myWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const err = t.throws(() => {
new WriterCollection({
name: "myCollection",
writerMapping: {
- "": myWriter
- }
+ "": myWriter,
+ },
});
});
t.is(err.message, "Empty path in path mapping of WriterCollection myCollection",
@@ -70,14 +70,14 @@ test("Constructor: Throws for empty path", (t) => {
test("Constructor: Throws for missing leading slash", (t) => {
const myWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const err = t.throws(() => {
new WriterCollection({
name: "myCollection",
writerMapping: {
- "my/path/": myWriter
- }
+ "my/path/": myWriter,
+ },
});
});
t.is(err.message, "Missing leading slash in path mapping 'my/path/' of WriterCollection myCollection",
@@ -86,14 +86,14 @@ test("Constructor: Throws for missing leading slash", (t) => {
test("Constructor: Throws for missing trailing slash", (t) => {
const myWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const err = t.throws(() => {
new WriterCollection({
name: "myCollection",
writerMapping: {
- "/my/path": myWriter
- }
+ "/my/path": myWriter,
+ },
});
});
t.is(err.message, "Missing trailing slash in path mapping '/my/path' of WriterCollection myCollection",
@@ -102,34 +102,34 @@ test("Constructor: Throws for missing trailing slash", (t) => {
test("Write", async (t) => {
const myPathWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const myWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const generalWriter = {
- _write: sinon.stub()
+ _write: sinon.stub(),
};
const writerCollection = new WriterCollection({
name: "myCollection",
writerMapping: {
"/my/path/": myPathWriter,
"/my/": myWriter,
- "/": generalWriter
- }
+ "/": generalWriter,
+ },
});
const myPathResource = new Resource({
path: "/my/path/resource.res",
- string: "content"
+ string: "content",
});
const myResource = new Resource({
path: "/my/resource.res",
- string: "content"
+ string: "content",
});
const resource = new Resource({
path: "/resource.res",
- string: "content"
+ string: "content",
});
await writerCollection.write(myPathResource, "options 1");
@@ -150,21 +150,21 @@ test("Write", async (t) => {
test("byGlob", async (t) => {
const myPathWriter = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const myWriter = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const generalWriter = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const writerCollection = new WriterCollection({
name: "myCollection",
writerMapping: {
"/my/path/": myPathWriter,
"/my/": myWriter,
- "/": generalWriter
- }
+ "/": generalWriter,
+ },
});
await writerCollection.byGlob("/**");
@@ -180,21 +180,21 @@ test("byGlob", async (t) => {
test("byPath", async (t) => {
const myPathWriter = {
- _byPath: sinon.stub().resolves(null)
+ _byPath: sinon.stub().resolves(null),
};
const myWriter = {
- _byPath: sinon.stub().resolves(null)
+ _byPath: sinon.stub().resolves(null),
};
const generalWriter = {
- _byPath: sinon.stub().resolves(null)
+ _byPath: sinon.stub().resolves(null),
};
const writerCollection = new WriterCollection({
name: "myCollection",
writerMapping: {
"/my/path/": myPathWriter,
"/my/": myWriter,
- "/": generalWriter
- }
+ "/": generalWriter,
+ },
});
await writerCollection.byPath("/my/resource.res");
diff --git a/test/lib/adapters/AbstractAdapter.js b/test/lib/adapters/AbstractAdapter.ts
similarity index 81%
rename from test/lib/adapters/AbstractAdapter.js
rename to test/lib/adapters/AbstractAdapter.ts
index 16a06708..e4758e08 100644
--- a/test/lib/adapters/AbstractAdapter.js
+++ b/test/lib/adapters/AbstractAdapter.ts
@@ -1,45 +1,47 @@
import test from "ava";
-import AbstractAdapter from "../../../lib/adapters/AbstractAdapter.js";
-import {createResource} from "../../../lib/resourceFactory.js";
+import AbstractAdapter from "../../../src/adapters/AbstractAdapter.js";
+import {createResource} from "../../../src/resourceFactory.js";
+import {type LegacyResource} from "../../../src/Resource.js";
+import {type Project} from "@ui5/project/specifications/Project";
class MyAbstractAdapter extends AbstractAdapter { }
test("Missing paramter: virBasePath", (t) => {
t.throws(() => {
- new MyAbstractAdapter({});
+ new MyAbstractAdapter({} as {virBasePath: string; excludes?: string[]; project?: Project});
}, {
- message: "Unable to create adapter: Missing parameter 'virBasePath'"
+ message: "Unable to create adapter: Missing parameter 'virBasePath'",
}, "Threw with expected error message");
});
test("virBasePath must be absolute", (t) => {
t.throws(() => {
new MyAbstractAdapter({
- virBasePath: "foo"
+ virBasePath: "foo",
});
}, {
- message: "Unable to create adapter: Virtual base path must be absolute but is 'foo'"
+ message: "Unable to create adapter: Virtual base path must be absolute but is 'foo'",
}, "Threw with expected error message");
});
test("virBasePath must end with a slash", (t) => {
t.throws(() => {
new MyAbstractAdapter({
- virBasePath: "/foo"
+ virBasePath: "/foo",
});
}, {
- message: "Unable to create adapter: Virtual base path must end with a slash but is '/foo'"
+ message: "Unable to create adapter: Virtual base path must end with a slash but is '/foo'",
}, "Threw with expected error message");
});
test("_migrateResource", async (t) => {
// Any JS object which might be a kind of resource
- const resource = {
- _path: "/test.js"
+ const resource: LegacyResource = {
+ _path: "/test.js",
};
const writer = new MyAbstractAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const migratedResource = await writer._migrateResource(resource);
@@ -52,16 +54,16 @@ test("_assignProjectToResource: Resource is already assigned to another project
path: "/test.js",
project: {
getName: () => "test.lib",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
const writer = new MyAbstractAdapter({
virBasePath: "/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
const error = t.throws(() => writer._assignProjectToResource(resource));
@@ -74,8 +76,8 @@ test("_isPathHandled", (t) => {
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.true(writer._isPathHandled("/dest2/writer/test.js"), "Returned expected result");
@@ -90,8 +92,8 @@ test("_resolveVirtualPathToBase (read mode)", (t) => {
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.is(writer._resolveVirtualPathToBase("/dest2/writer/test.js"), "test.js", "Returned expected path");
@@ -106,8 +108,8 @@ test("_resolveVirtualPathToBase (read mode): Path does not starting with path co
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.is(writer._resolveVirtualPathToBase("/dest2/tmp/test.js"), null, "Returned null");
@@ -121,13 +123,13 @@ test("_resolveVirtualPathToBase (read mode): Path Must be absolute", (t) => {
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.throws(() => writer._resolveVirtualPathToBase("./dest2/write"), {
message:
- `Failed to resolve virtual path './dest2/write': Path must be absolute`
+ `Failed to resolve virtual path './dest2/write': Path must be absolute`,
}, "Threw with expected error message");
});
@@ -136,8 +138,8 @@ test("_resolveVirtualPathToBase (write mode)", (t) => {
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.is(writer._resolveVirtualPathToBase("/dest2/writer/test.js", true), "test.js", "Returned expected path");
@@ -153,32 +155,32 @@ test("_resolveVirtualPathToBase (write mode): Path does not starting with path c
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.throws(() => writer._resolveVirtualPathToBase("/dest2/tmp/test.js", true), {
message:
`Failed to write resource with virtual path '/dest2/tmp/test.js': ` +
- `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`
+ `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`,
}, "Threw with expected error message");
t.throws(() => writer._resolveVirtualPathToBase("/dest2/writer/../reader", true), {
message:
`Failed to write resource with virtual path '/dest2/writer/../reader': ` +
- `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`
+ `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`,
}, "Threw with expected error message");
t.throws(() => writer._resolveVirtualPathToBase("/dest2/write", true), {
message:
`Failed to write resource with virtual path '/dest2/write': ` +
- `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`
+ `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`,
}, "Threw with expected error message");
t.throws(() => writer._resolveVirtualPathToBase("/..//write", true), {
message:
`Failed to write resource with virtual path '/..//write': ` +
- `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`
+ `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'`,
}, "Threw with expected error message");
});
@@ -187,54 +189,54 @@ test("_resolveVirtualPathToBase (write mode): Path Must be absolute", (t) => {
virBasePath: "/dest2/writer/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.throws(() => writer._resolveVirtualPathToBase("./dest2/write", true), {
message:
- `Failed to resolve virtual path './dest2/write': Path must be absolute`
+ `Failed to resolve virtual path './dest2/write': Path must be absolute`,
}, "Threw with expected error message");
});
-test("_normalizePattern", async (t) => {
+test("_normalizePattern", (t) => {
const writer = new MyAbstractAdapter({
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
- t.deepEqual(await writer._normalizePattern("/*/{src,test}/**"), [
+ t.deepEqual(writer._normalizePattern("/*/{src,test}/**"), [
"src/**",
- "test/**"
+ "test/**",
], "Returned expected patterns");
});
-test("_normalizePattern: Match base directory", async (t) => {
+test("_normalizePattern: Match base directory", (t) => {
const writer = new MyAbstractAdapter({
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
- t.deepEqual(await writer._normalizePattern("/*"), [""],
+ t.deepEqual(writer._normalizePattern("/*"), [""],
"Returned an empty pattern since the input pattern matches the base directory only");
});
-test("_normalizePattern: Match sub-directory", async (t) => {
+test("_normalizePattern: Match sub-directory", (t) => {
const writer = new MyAbstractAdapter({
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
- t.deepEqual(await writer._normalizePattern("/path/*"), ["*"],
+ t.deepEqual(writer._normalizePattern("/path/*"), ["*"],
"Returned expected patterns");
});
@@ -243,8 +245,8 @@ test("_normalizePattern: Match all", (t) => {
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.deepEqual(writer._normalizePattern("/**/*"), ["**/*"],
@@ -256,8 +258,8 @@ test("_normalizePattern: Relative path segment above virtual root directory", (t
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.deepEqual(writer._normalizePattern("/path/../../*"), [],
@@ -269,8 +271,8 @@ test("_normalizePattern: Relative path segment resolving to base directory", (t)
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.deepEqual(writer._normalizePattern("/*/../*"), [""],
@@ -282,8 +284,8 @@ test("_normalizePattern: Relative path segment", (t) => {
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.deepEqual(writer._normalizePattern("/path/../*"), [""],
@@ -295,8 +297,8 @@ test("_normalizePattern: Relative path segment within base directory, matching a
virBasePath: "/path/",
project: {
getName: () => "test.lib1",
- getVersion: () => "2.0.0"
- }
+ getVersion: () => "2.0.0",
+ } as Project,
});
t.deepEqual(writer._normalizePattern("/path/path2/../**/*"), ["**/*"],
diff --git a/test/lib/adapters/FileSystem.js b/test/lib/adapters/FileSystem.ts
similarity index 72%
rename from test/lib/adapters/FileSystem.js
rename to test/lib/adapters/FileSystem.ts
index 9e41965b..fa45bdca 100644
--- a/test/lib/adapters/FileSystem.js
+++ b/test/lib/adapters/FileSystem.ts
@@ -1,13 +1,13 @@
import test from "ava";
-import FileSystem from "../../../lib/adapters/FileSystem.js";
+import FileSystem from "../../../src/adapters/FileSystem.js";
test.serial("Missing parameter: fsBasePath", (t) => {
t.throws(() => {
new FileSystem({
- virBasePath: "/"
+ virBasePath: "/",
});
}, {
- message: "Unable to create adapter: Missing parameter 'fsBasePath'"
+ message: "Unable to create adapter: Missing parameter 'fsBasePath'",
}, "Threw with expected error message");
});
diff --git a/test/lib/adapters/FileSystem_read.js b/test/lib/adapters/FileSystem_read.ts
similarity index 91%
rename from test/lib/adapters/FileSystem_read.js
rename to test/lib/adapters/FileSystem_read.ts
index f5bee36f..e7f6c754 100644
--- a/test/lib/adapters/FileSystem_read.js
+++ b/test/lib/adapters/FileSystem_read.ts
@@ -2,12 +2,12 @@ import test from "ava";
import sinon from "sinon";
import esmock from "esmock";
import path from "node:path";
-import {createAdapter} from "../../../lib/resourceFactory.js";
+import {createAdapter} from "../../../src/resourceFactory.js";
test("glob resources from application.a w/ virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob("/app/**/*.html");
@@ -17,7 +17,7 @@ test("glob resources from application.a w/ virtual base path prefix", async (t)
test("glob resources from application.a w/o virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob("/**/*.html");
@@ -27,7 +27,7 @@ test("glob resources from application.a w/o virtual base path prefix", async (t)
test("glob virtual directory w/o virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob("/*", {nodir: false});
@@ -37,12 +37,12 @@ test("glob virtual directory w/o virtual base path prefix", async (t) => {
test("glob virtual directory w/o virtual base path prefix and multiple patterns", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob([
"/*",
- "/*"
+ "/*",
], {nodir: false});
t.is(resources.length, 1, "Found exactly one resource");
});
@@ -50,7 +50,7 @@ test("glob virtual directory w/o virtual base path prefix and multiple patterns"
test("glob virtual directory w/ virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/app/*", {nodir: false});
@@ -60,7 +60,7 @@ test("glob virtual directory w/ virtual base path prefix", async (t) => {
test("glob virtual directory w/o virtual base path prefix and nodir: true", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob("/*", {nodir: true});
@@ -70,12 +70,12 @@ test("glob virtual directory w/o virtual base path prefix and nodir: true", asyn
test("glob virtual directory w/o virtual base path prefix and nodir: true and multiple patterns", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resources = await readerWriter.byGlob([
"/*",
- "/*"
+ "/*",
], {nodir: true});
t.is(resources.length, 0, "Found no resources");
});
@@ -83,7 +83,7 @@ test("glob virtual directory w/o virtual base path prefix and nodir: true and mu
test("glob virtual directory w/ virtual base path prefix and nodir: true", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/app/*", {nodir: true});
@@ -93,10 +93,10 @@ test("glob virtual directory w/ virtual base path prefix and nodir: true", async
test("glob resources from application.a with directory exclude", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
- await readerWriter.byGlob("/!(pony,unicorn)/**").then(function(resources) {
+ await readerWriter.byGlob("/!(pony,unicorn)/**").then(function (resources) {
t.is(resources.length, 2, "Found exactly two resource");
});
});
@@ -104,7 +104,7 @@ test("glob resources from application.a with directory exclude", async (t) => {
test("glob all above virtual base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const res = await readerWriter.byGlob([
@@ -116,7 +116,7 @@ test("glob all above virtual base path (path traversal)", async (t) => {
test("glob virtual directory above virtual base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const res = await readerWriter.byGlob([
@@ -134,7 +134,7 @@ test("glob not existing directory with existing file path (nodir: true)", async
// globby will throw if it is attempted to glob the content of a directory
// which is actually a file
await t.throwsAsync(srcReader.byGlob("/test/library/l/Test.html/*", {nodir: true}), {
- message: /ENOTDIR/
+ message: /ENOTDIR/,
}, "Threw with expected error message");
});
@@ -147,14 +147,14 @@ test("glob not existing directory with existing file path (nodir: false)", async
// globby will throw if it is attempted to glob the content of a directory
// which is actually a file
await t.throwsAsync(srcReader.byGlob("/test/library/l/Test.html/*", {nodir: false}), {
- message: /ENOTDIR/
+ message: /ENOTDIR/,
}, "Threw with expected error message");
});
test("byPath", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -164,7 +164,7 @@ test("byPath", async (t) => {
test("byPath virtual directory above base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources/app/../package.json", {nodir: true});
@@ -174,7 +174,7 @@ test("byPath virtual directory above base path (path traversal)", async (t) => {
test("byPath: Root dir w/ trailing slash", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources/app/", {nodir: false});
@@ -184,7 +184,7 @@ test("byPath: Root dir w/ trailing slash", async (t) => {
test("byPath: Root dir w/o trailing slash", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources/app", {nodir: false});
@@ -194,7 +194,7 @@ test("byPath: Root dir w/o trailing slash", async (t) => {
test("byPath: Virtual directory w/ trailing slash", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources/", {nodir: false});
@@ -204,7 +204,7 @@ test("byPath: Virtual directory w/ trailing slash", async (t) => {
test("byPath: Virtual directory w/o trailing slash", async (t) => {
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const resource = await readerWriter.byPath("/resources", {nodir: false});
@@ -216,7 +216,7 @@ test("byPath: Incorrect virtual directory w/o trailing slash", async (t) => {
// tested elsewhere
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
// TODO: This should actually not match
@@ -240,13 +240,13 @@ test("static excludes: glob library src and test", async (t) => {
const srcReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
- excludes
+ excludes,
});
const testReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/test",
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
const srcResources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -254,7 +254,7 @@ test("static excludes: glob library src and test", async (t) => {
t.is(srcResources.length, 1, "Found one src resource");
t.deepEqual(srcResources.map(getPathFromResource), [
- "/resources/library/l/.library"
+ "/resources/library/l/.library",
], "Found expected src resources");
t.is(testResources.length, 0, "Found no test resources");
@@ -271,13 +271,13 @@ test("static excludes: glob library src and test with double negation", async (t
const srcReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
- excludes
+ excludes,
});
const testReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/test",
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
const srcResources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -285,12 +285,12 @@ test("static excludes: glob library src and test with double negation", async (t
t.is(srcResources.length, 1, "Found one src resource");
t.deepEqual(srcResources.map(getPathFromResource), [
- "/resources/library/l/.library"
+ "/resources/library/l/.library",
], "Found expected src resources");
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -305,14 +305,14 @@ test("static excludes: glob library test with double negation", async (t) => {
const testReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/test",
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
const testResources = await testReader.byGlob("/**/*", {nodir: true});
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -321,8 +321,8 @@ test("static excludes: glob with virtual root exclude", async (t) => {
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/**"
- ]
+ "/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -335,8 +335,8 @@ test("static excludes: glob with negated directory exclude, excluding resources"
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -349,8 +349,8 @@ test("static excludes: glob with negated directory exclude, not excluding resour
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -362,7 +362,7 @@ test("static excludes: byPath exclude everything in sub-directory", async (t) =>
const readerWriter = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
- excludes: ["/resources/app/**"]
+ excludes: ["/resources/app/**"],
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -375,8 +375,8 @@ test("static excludes: byPath exclude with negation", async (t) => {
virBasePath: "/resources/app/",
excludes: [
"/resources/app/**",
- "!/resources/app/index.html"
- ]
+ "!/resources/app/index.html",
+ ],
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -389,8 +389,8 @@ test("static excludes: byPath exclude with unused negation", async (t) => {
virBasePath: "/resources/app/",
excludes: [
"!/resources/app/index.html",
- "/resources/app/**"
- ]
+ "/resources/app/**",
+ ],
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -402,8 +402,8 @@ test("static excludes: byPath exclude with negated directory pattern, excluding
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -415,8 +415,8 @@ test("static excludes: byPath exclude with negated directory pattern, not exclud
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true});
@@ -430,14 +430,14 @@ test("byPath: exclude with unused negation", async (t) => {
excludes: [
"!/resources/app/i18n/**",
"/resources/app/**",
- "!/resources/app/manifest.json"
- ]
+ "!/resources/app/manifest.json",
+ ],
});
const [manifest, i18n, i18ni18n] = await Promise.all([
readerWriter.byPath("/resources/app/manifest.json", {nodir: true}),
readerWriter.byPath("/resources/app/i18n.properties", {nodir: true}),
- readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true})
+ readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true}),
]);
t.truthy(manifest, "Found manifest.json resource");
t.is(i18n, null, "i18n resource is excluded");
@@ -455,13 +455,13 @@ test("static excludes: glob library src and test with double negation (nodir: fa
const srcReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
- excludes
+ excludes,
});
const testReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/test",
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
const srcResources = await srcReader.byGlob("/**/*", {nodir: false});
@@ -471,7 +471,7 @@ test("static excludes: glob library src and test with double negation (nodir: fa
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -480,8 +480,8 @@ test("static excludes: glob with virtual root exclude (nodir: false)", async (t)
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/**"
- ]
+ "/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: false});
@@ -493,8 +493,8 @@ test("static excludes: glob with negated directory exclude, excluding resources
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: false});
@@ -507,8 +507,8 @@ test("static excludes: glob with negated directory exclude, not excluding resour
fsBasePath: "./test/fixtures/library.l/src",
virBasePath: "/resources/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
const resources = await srcReader.byGlob("/**/*", {nodir: false});
@@ -521,8 +521,8 @@ test("static excludes: glob with exclude (nodir: false)", async (t) => {
fsBasePath: "./test/fixtures/library.l/",
virBasePath: "/",
excludes: [
- "/test/**"
- ]
+ "/test/**",
+ ],
});
const resources = await srcReader.byGlob("/test/library/l/Test.html", {nodir: false});
@@ -534,7 +534,7 @@ test("glob with useGitignore: true", async (t) => {
const srcReader = createAdapter({
fsBasePath: "./test/fixtures/library.l/",
virBasePath: "/",
- useGitignore: true
+ useGitignore: true,
});
const resources = await srcReader.byGlob("/**/*");
@@ -553,17 +553,17 @@ test("byPath with useGitignore: true", async (t) => {
const {isGitIgnored} = await import("globby");
const isGitIgnoredSpy = sinon.stub().callsFake(isGitIgnored);
- const FileSystem = await esmock("../../../lib/adapters/FileSystem.js", {
- "globby": {
- isGitIgnored: isGitIgnoredSpy
- }
+ const FileSystem = await esmock("../../../src/adapters/FileSystem.js", {
+ globby: {
+ isGitIgnored: isGitIgnoredSpy,
+ },
});
const fsBasePath = "./test/fixtures/library.l/";
const srcReader = new FileSystem({
fsBasePath,
virBasePath: "/",
- useGitignore: true
+ useGitignore: true,
});
const testResource = await srcReader.byPath("/test/library/l/Test.html");
diff --git a/test/lib/adapters/FileSystem_write.js b/test/lib/adapters/FileSystem_write.ts
similarity index 98%
rename from test/lib/adapters/FileSystem_write.js
rename to test/lib/adapters/FileSystem_write.ts
index 8386c2a5..c3f7eabe 100644
--- a/test/lib/adapters/FileSystem_write.js
+++ b/test/lib/adapters/FileSystem_write.ts
@@ -6,7 +6,7 @@ import test from "ava";
import {rimraf} from "rimraf";
import sinon from "sinon";
-import {createAdapter, createResource} from "../../../lib/resourceFactory.js";
+import {createAdapter, createResource} from "../../../src/resourceFactory.js";
function getFileContent(path) {
return readFile(path, "utf8");
@@ -33,12 +33,12 @@ test.beforeEach(async (t) => {
t.context.readerWriters = {
source: createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
}),
dest: createAdapter({
fsBasePath: "./test/tmp/adapters/FileSystemWrite/" + tmpDirName,
- virBasePath: "/app/"
- })
+ virBasePath: "/app/",
+ }),
};
});
@@ -127,7 +127,7 @@ test("Write with readOnly and drain options set should fail", async (t) => {
// Write resource content to another readerWriter
await t.throwsAsync(readerWriters.dest.write(resource, {readOnly: true, drain: true}), {
message: "Error while writing resource /app/index.html: " +
- "Do not use options 'drain' and 'readOnly' at the same time."
+ "Do not use options 'drain' and 'readOnly' at the same time.",
});
});
diff --git a/test/lib/adapters/FileSystem_write_large_file.js b/test/lib/adapters/FileSystem_write_large_file.ts
similarity index 89%
rename from test/lib/adapters/FileSystem_write_large_file.js
rename to test/lib/adapters/FileSystem_write_large_file.ts
index bb8fbfb6..cea40f69 100644
--- a/test/lib/adapters/FileSystem_write_large_file.js
+++ b/test/lib/adapters/FileSystem_write_large_file.ts
@@ -3,8 +3,8 @@ import {fileURLToPath} from "node:url";
import {Buffer} from "node:buffer";
import {readFile} from "node:fs/promises";
-import FileSystem from "../../../lib/adapters/FileSystem.js";
-import Resource from "../../../lib/Resource.js";
+import FileSystem from "../../../src/adapters/FileSystem.js";
+import Resource from "../../../src/Resource.js";
import path from "node:path";
test.serial("Stream a large file from source to target", async (t) => {
@@ -16,14 +16,14 @@ test.serial("Stream a large file from source to target", async (t) => {
const fileSystem = new FileSystem({
fsBasePath,
- virBasePath: "/"
+ virBasePath: "/",
});
const largeBuffer = Buffer.alloc(1048576); // 1MB
await fileSystem.write(new Resource({
path: "/large-file.txt",
- buffer: largeBuffer
+ buffer: largeBuffer,
}));
t.deepEqual(await readFile(path.join(fsBasePath, "large-file.txt")), largeBuffer,
diff --git a/test/lib/adapters/Memory_read.js b/test/lib/adapters/Memory_read.ts
similarity index 91%
rename from test/lib/adapters/Memory_read.js
rename to test/lib/adapters/Memory_read.ts
index 70a034b2..9e468cbe 100644
--- a/test/lib/adapters/Memory_read.js
+++ b/test/lib/adapters/Memory_read.ts
@@ -1,5 +1,5 @@
import test from "ava";
-import {createAdapter, createResource} from "../../../lib/resourceFactory.js";
+import {createAdapter, createResource} from "../../../src/resourceFactory.js";
async function fillFromFs(readerWriter, {fsBasePath = "./test/fixtures/glob", virBasePath = "/app/"} = {}) {
const fsReader = createAdapter({
@@ -8,7 +8,7 @@ async function fillFromFs(readerWriter, {fsBasePath = "./test/fixtures/glob", vi
});
const fsResources = await fsReader.byGlob("/**/*");
- return Promise.all(fsResources.map(function(resource) {
+ return Promise.all(fsResources.map(function (resource) {
return readerWriter.write(resource);
}));
}
@@ -20,6 +20,7 @@ function matchGlobResult(t, resources, expectedResources) {
return resource.getPath();
});
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < expectedResources.length; i++) {
const expectedResource = expectedResources[i];
t.true(
@@ -31,12 +32,12 @@ function matchGlobResult(t, resources, expectedResources) {
test("glob resources from application.a w/ virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resource = createResource({
path: "/app/index.html",
- string: "test"
+ string: "test",
});
await readerWriter.write(resource);
@@ -46,12 +47,12 @@ test("glob resources from application.a w/ virtual base path prefix", async (t)
test("glob resources from application.a w/o virtual base path prefix", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const resource = createResource({
path: "/app/index.html",
- string: "test"
+ string: "test",
});
await readerWriter.write(resource);
@@ -62,7 +63,7 @@ test("glob resources from application.a w/o virtual base path prefix", async (t)
test("glob virtual directory w/o virtual base path prefix", async (t) => {
// TODO: Add similar test (globbing on empty directory) for FS RL
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/*", {nodir: false});
@@ -72,12 +73,12 @@ test("glob virtual directory w/o virtual base path prefix", async (t) => {
test("glob virtual directory w/o virtual base path prefix and multiple patterns", async (t) => {
// TODO: Add similar test (globbing on empty directory) for FS RL
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob([
"/*",
- "/*"
+ "/*",
], {nodir: false});
t.is(resources.length, 1, "Found exactly one resource");
});
@@ -85,7 +86,7 @@ test("glob virtual directory w/o virtual base path prefix and multiple patterns"
test("glob virtual directory w/ virtual base path prefix", async (t) => {
// TODO: Add similar test (globbing on empty directory) for FS RL
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/app/*", {nodir: false});
@@ -94,7 +95,7 @@ test("glob virtual directory w/ virtual base path prefix", async (t) => {
test("glob virtual directory w/o virtual base path prefix and nodir: true", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/*", {nodir: true});
@@ -103,7 +104,7 @@ test("glob virtual directory w/o virtual base path prefix and nodir: true", asyn
test("glob virtual directory w/ virtual base path prefix and nodir: true", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob("/app/*", {nodir: true});
@@ -112,12 +113,12 @@ test("glob virtual directory w/ virtual base path prefix and nodir: true", async
test("glob virtual directory w/ virtual base path prefix and nodir: true and multiple patterns", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/one/two/"
+ virBasePath: "/app/one/two/",
});
const resources = await readerWriter.byGlob([
"/*",
- "/*"
+ "/*",
], {nodir: true});
t.is(resources.length, 0, "Found no resources");
});
@@ -125,7 +126,7 @@ test("glob virtual directory w/ virtual base path prefix and nodir: true and mul
/* Load more data from FS into memory */
test("glob all", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
const resources = await readerWriter.byGlob("/**/*.*");
@@ -135,7 +136,7 @@ test("glob all", async (t) => {
test("glob all from root", async (t) => {
t.plan(2);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
const resources = await readerWriter.byGlob("/*/*.*");
@@ -144,7 +145,7 @@ test("glob all from root", async (t) => {
test("glob all with virtual path included", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -155,7 +156,7 @@ test("glob all with virtual path included", async (t) => {
test("glob a specific filetype (yaml)", async (t) => {
t.plan(2);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -168,7 +169,7 @@ test("glob a specific filetype (yaml)", async (t) => {
test("glob two specific filetype (yaml and js)", async (t) => {
t.plan(4);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -176,7 +177,7 @@ test("glob two specific filetype (yaml and js)", async (t) => {
const expectedFiles = [
"/app/application.b/ui5.yaml",
"/app/application.a/ui5.yaml",
- "/app/application.a/webapp/test.js"
+ "/app/application.a/webapp/test.js",
];
matchGlobResult(t, resources, expectedFiles);
});
@@ -184,17 +185,17 @@ test("glob two specific filetype (yaml and js)", async (t) => {
test("glob a specific filetype (json) with exclude pattern", async (t) => {
t.plan(3);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
const resources = await readerWriter.byGlob([
"/**/*.json",
- "!/**/*package.json"
+ "!/**/*package.json",
]);
const expectedFiles = [
"/app/application.b/webapp/manifest.json",
- "/app/application.b/webapp/embedded/manifest.json"
+ "/app/application.b/webapp/embedded/manifest.json",
];
matchGlobResult(t, resources, expectedFiles);
});
@@ -202,14 +203,14 @@ test("glob a specific filetype (json) with exclude pattern", async (t) => {
test("glob a specific filetype (json) with multiple exclude pattern", async (t) => {
t.plan(2);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
const resources = await readerWriter.byGlob([
"/**/*.json",
"!/**/*package.json",
- "!/**/embedded/manifest.json"
+ "!/**/embedded/manifest.json",
]);
matchGlobResult(t, resources, ["/app/application.b/webapp/manifest.json"]);
});
@@ -217,7 +218,7 @@ test("glob a specific filetype (json) with multiple exclude pattern", async (t)
test("glob (normalized) root directory (=> fs root)", async (t) => {
t.plan(2);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -233,7 +234,7 @@ test("glob (normalized) root directory (=> fs root)", async (t) => {
test("glob root directory", async (t) => {
t.plan(2);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -244,7 +245,7 @@ test("glob root directory", async (t) => {
test("glob subdirectory", async (t) => {
t.plan(3);
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter);
@@ -259,7 +260,7 @@ test("glob subdirectory", async (t) => {
test("glob all resources above virtual base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -274,7 +275,7 @@ test("glob all resources above virtual base path (path traversal)", async (t) =>
test("glob virtual directory above virtual base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -289,7 +290,7 @@ test("glob virtual directory above virtual base path (path traversal)", async (t
test("byPath", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -302,7 +303,7 @@ test("byPath", async (t) => {
test("byPath virtual directory above base path (path traversal)", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -313,7 +314,6 @@ test("byPath virtual directory above base path (path traversal)", async (t) => {
t.is(resource, null, "Found no resource");
});
-
function getPathFromResource(resource) {
return resource.getPath();
}
@@ -329,7 +329,7 @@ test("static excludes: glob library src and test", async (t) => {
];
const srcReader = createAdapter({
virBasePath: "/resources/",
- excludes
+ excludes,
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -338,7 +338,7 @@ test("static excludes: glob library src and test", async (t) => {
const testReader = createAdapter({
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
await fillFromFs(testReader, {
fsBasePath: "./test/fixtures/library.l/test",
@@ -350,7 +350,7 @@ test("static excludes: glob library src and test", async (t) => {
t.is(srcResources.length, 1, "Found one src resource");
t.deepEqual(srcResources.map(getPathFromResource), [
- "/resources/library/l/.library"
+ "/resources/library/l/.library",
], "Found expected src resources");
t.is(testResources.length, 0, "Found no test resources");
@@ -366,7 +366,7 @@ test("static excludes: glob library src and test with double negation", async (t
];
const srcReader = createAdapter({
virBasePath: "/resources/",
- excludes
+ excludes,
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -375,11 +375,11 @@ test("static excludes: glob library src and test with double negation", async (t
const testReader = createAdapter({
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
await fillFromFs(testReader, {
fsBasePath: "./test/fixtures/library.l/test",
- virBasePath: "/test-resources/"
+ virBasePath: "/test-resources/",
});
const srcResources = await srcReader.byGlob("/**/*", {nodir: true});
@@ -387,12 +387,12 @@ test("static excludes: glob library src and test with double negation", async (t
t.is(srcResources.length, 1, "Found one src resource");
t.deepEqual(srcResources.map(getPathFromResource), [
- "/resources/library/l/.library"
+ "/resources/library/l/.library",
], "Found expected src resources");
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -406,18 +406,18 @@ test("static excludes: glob library test with double negation", async (t) => {
const testReader = createAdapter({
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
await fillFromFs(testReader, {
fsBasePath: "./test/fixtures/library.l/test",
- virBasePath: "/test-resources/"
+ virBasePath: "/test-resources/",
});
const testResources = await testReader.byGlob("/**/*", {nodir: true});
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -425,8 +425,8 @@ test("static excludes: glob with virtual root exclude", async (t) => {
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/**"
- ]
+ "/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -442,8 +442,8 @@ test("static excludes: glob with negated directory exclude, excluding resources"
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -459,8 +459,8 @@ test("static excludes: glob with negated directory exclude, not excluding resour
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -475,7 +475,7 @@ test("static excludes: glob with negated directory exclude, not excluding resour
test("static excludes: byPath exclude everything in sub-directory", async (t) => {
const readerWriter = createAdapter({
virBasePath: "/resources/app/",
- excludes: ["/resources/app/**"]
+ excludes: ["/resources/app/**"],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -491,8 +491,8 @@ test("static excludes: byPath exclude with negation", async (t) => {
virBasePath: "/resources/app/",
excludes: [
"/resources/app/**",
- "!/resources/app/index.html"
- ]
+ "!/resources/app/index.html",
+ ],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -508,8 +508,8 @@ test("static excludes: byPath exclude with unused negation", async (t) => {
virBasePath: "/resources/app/",
excludes: [
"!/resources/app/index.html",
- "/resources/app/**"
- ]
+ "/resources/app/**",
+ ],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -524,8 +524,8 @@ test("static excludes: byPath exclude with negated directory pattern, excluding
const readerWriter = createAdapter({
virBasePath: "/resources/app/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -540,8 +540,8 @@ test("static excludes: byPath exclude with negated directory pattern, not exclud
const readerWriter = createAdapter({
virBasePath: "/resources/app/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.a/webapp",
@@ -558,8 +558,8 @@ test("byPath: exclude with unused negation", async (t) => {
excludes: [
"!/resources/app/i18n/**",
"/resources/app/**",
- "!/resources/app/manifest.json"
- ]
+ "!/resources/app/manifest.json",
+ ],
});
await fillFromFs(readerWriter, {
fsBasePath: "./test/fixtures/application.b/webapp",
@@ -569,7 +569,7 @@ test("byPath: exclude with unused negation", async (t) => {
const [manifest, i18n, i18ni18n] = await Promise.all([
readerWriter.byPath("/resources/app/manifest.json", {nodir: true}),
readerWriter.byPath("/resources/app/i18n.properties", {nodir: true}),
- readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true})
+ readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true}),
]);
t.truthy(manifest, "Found manifest.json resource");
t.is(i18n, null, "i18n resource is excluded");
@@ -586,7 +586,7 @@ test("static excludes: glob library src and test with double negation (nodir: fa
];
const srcReader = createAdapter({
virBasePath: "/resources/",
- excludes
+ excludes,
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -595,7 +595,7 @@ test("static excludes: glob library src and test with double negation (nodir: fa
const testReader = createAdapter({
virBasePath: "/test-resources/",
- excludes
+ excludes,
});
await fillFromFs(testReader, {
fsBasePath: "./test/fixtures/library.l/test",
@@ -609,7 +609,7 @@ test("static excludes: glob library src and test with double negation (nodir: fa
t.is(testResources.length, 1, "Found one test resource");
t.deepEqual(testResources.map(getPathFromResource), [
- "/test-resources/library/l/Test2.html"
+ "/test-resources/library/l/Test2.html",
], "Found expected test resources");
});
@@ -617,8 +617,8 @@ test("static excludes: glob with virtual root exclude (nodir: false)", async (t)
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/**"
- ]
+ "/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -633,8 +633,8 @@ test("static excludes: glob with negated directory exclude, excluding resources
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/!({pony,unicorn})/**"
- ]
+ "/!({pony,unicorn})/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -650,8 +650,8 @@ test("static excludes: glob with negated directory exclude, not excluding resour
const srcReader = createAdapter({
virBasePath: "/resources/",
excludes: [
- "/!(resources)/**"
- ]
+ "/!(resources)/**",
+ ],
});
await fillFromFs(srcReader, {
fsBasePath: "./test/fixtures/library.l/src",
@@ -666,7 +666,7 @@ test("static excludes: glob with negated directory exclude, not excluding resour
test("byPath returns new resource", async (t) => {
const originalResource = createResource({
path: "/app/index.html",
- string: "test"
+ string: "test",
});
const memoryAdapter = createAdapter({virBasePath: "/"});
@@ -694,7 +694,7 @@ test("byPath returns new resource", async (t) => {
test("byGlob returns new resources", async (t) => {
const originalResource = createResource({
path: "/app/index.html",
- string: "test"
+ string: "test",
});
const memoryAdapter = createAdapter({virBasePath: "/"});
diff --git a/test/lib/adapters/Memory_write.js b/test/lib/adapters/Memory_write.ts
similarity index 85%
rename from test/lib/adapters/Memory_write.js
rename to test/lib/adapters/Memory_write.ts
index d1cbad8a..437eddb8 100644
--- a/test/lib/adapters/Memory_write.js
+++ b/test/lib/adapters/Memory_write.ts
@@ -1,14 +1,14 @@
import test from "ava";
-import {createAdapter, createResource} from "../../../lib/resourceFactory.js";
+import {createAdapter, createResource} from "../../../src/resourceFactory.js";
import sinon from "sinon";
test("glob resources from application.a w/ virtual base path prefix", async (t) => {
const dest = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const res = createResource({
- path: "/app/index.html"
+ path: "/app/index.html",
});
await dest.write(res);
const resources = await dest.byGlob("/app/*.html");
@@ -18,11 +18,11 @@ test("glob resources from application.a w/ virtual base path prefix", async (t)
test("glob resources from application.a w/o virtual base path prefix", async (t) => {
const dest = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const res = createResource({
- path: "/app/index.html"
+ path: "/app/index.html",
});
await dest.write(res);
const resources = await dest.byGlob("/**/*.html");
@@ -31,16 +31,16 @@ test("glob resources from application.a w/o virtual base path prefix", async (t)
test("Write resource w/ virtual base path", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const res = createResource({
- path: "/app/test.html"
+ path: "/app/test.html",
});
await readerWriter.write(res);
t.deepEqual(readerWriter._virFiles, {
- "test.html": res
+ "test.html": res,
}, "Adapter added resource with correct path");
t.deepEqual(Object.keys(readerWriter._virDirs), [], "Adapter added correct virtual directories");
@@ -49,22 +49,22 @@ test("Write resource w/ virtual base path", async (t) => {
test("Write resource w/o virtual base path", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const res = createResource({
- path: "/one/two/three/test.html"
+ path: "/one/two/three/test.html",
});
await readerWriter.write(res);
t.deepEqual(readerWriter._virFiles, {
- "one/two/three/test.html": res
+ "one/two/three/test.html": res,
}, "Adapter added resource with correct path");
t.deepEqual(Object.keys(readerWriter._virDirs), [
"one/two/three",
"one/two",
- "one"
+ "one",
], "Adapter added correct virtual directories");
const dirRes = readerWriter._virDirs["one/two/three"];
@@ -74,22 +74,22 @@ test("Write resource w/o virtual base path", async (t) => {
test("Write resource w/ deep virtual base path", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/a/"
+ virBasePath: "/app/a/",
});
const res = createResource({
- path: "/app/a/one/two/three/test.html"
+ path: "/app/a/one/two/three/test.html",
});
await readerWriter.write(res);
t.deepEqual(readerWriter._virFiles, {
- "one/two/three/test.html": res
+ "one/two/three/test.html": res,
}, "Adapter added resource with correct path");
t.deepEqual(Object.keys(readerWriter._virDirs), [
"one/two/three",
"one/two",
- "one"
+ "one",
], "Adapter added correct virtual directories");
const dirRes = readerWriter._virDirs["one/two/three"];
@@ -99,32 +99,32 @@ test("Write resource w/ deep virtual base path", async (t) => {
test("Write resource w/ crazy virtual base path", async (t) => {
const readerWriter = createAdapter({
- virBasePath: "/app/🐛/"
+ virBasePath: "/app/🐛/",
});
const res = createResource({
- path: "/app/🐛/one\\/2/3️⃣/test"
+ path: "/app/🐛/one\\/2/3️⃣/test",
});
await readerWriter.write(res);
t.deepEqual(readerWriter._virFiles, {
- "one\\/2/3️⃣/test": res
+ "one\\/2/3️⃣/test": res,
}, "Adapter added resource with correct path");
t.deepEqual(Object.keys(readerWriter._virDirs), [
"one\\/2/3️⃣",
"one\\/2",
- "one\\"
+ "one\\",
], "Adapter added correct virtual directories");
});
test("Migration of resource is executed", async (t) => {
const writer = createAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const resource = createResource({
- path: "/test.js"
+ path: "/test.js",
});
const migrateResourceWriterSpy = sinon.spy(writer, "_migrateResource");
@@ -134,12 +134,12 @@ test("Migration of resource is executed", async (t) => {
test("Resource: Change instance after write", async (t) => {
const writer = createAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const resource = createResource({
path: "/test.js",
- string: "MyInitialContent"
+ string: "MyInitialContent",
});
await writer.write(resource);
diff --git a/test/lib/fsInterface.js b/test/lib/fsInterface.ts
similarity index 88%
rename from test/lib/fsInterface.js
rename to test/lib/fsInterface.ts
index 4d00222b..788ece66 100644
--- a/test/lib/fsInterface.js
+++ b/test/lib/fsInterface.ts
@@ -6,10 +6,10 @@ import {fileURLToPath} from "node:url";
import fsSync from "node:fs";
const stat = promisify(fsSync.stat);
import {readFile} from "node:fs/promises";
-import fsInterface from "../../lib/fsInterface.js";
-import MemAdapter from "../../lib/adapters/Memory.js";
-import FsAdapter from "../../lib/adapters/FileSystem.js";
-import Resource from "../../lib/Resource.js";
+import fsInterface from "../../src/fsInterface.js";
+import MemAdapter from "../../src/adapters/Memory.js";
+import FsAdapter from "../../src/adapters/FileSystem.js";
+import Resource from "../../src/Resource.js";
const assertReadFile = async (t, readFile, basepath, filepath, content) => {
content = content || "content of " + filepath;
@@ -39,7 +39,7 @@ function getPath() {
test("MemAdapter: readFile", async (t) => {
const memAdapter = new MemAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const fs = fsInterface(memAdapter);
const readFile = promisify(fs.readFile);
@@ -47,16 +47,16 @@ test("MemAdapter: readFile", async (t) => {
const fsPath = path.join("/", "foo.txt");
await memAdapter.write(new Resource({
path: "/foo.txt",
- string: `content of ${fsPath}`
+ string: `content of ${fsPath}`,
}));
- `content of ${fsPath}`;
+ // `content of ${fsPath}`;
await assertReadFile(t, readFile, "", fsPath);
});
test("FsAdapter: readFile with non-ASCII characters in path", async (t) => {
const fsAdapter = new FsAdapter({
virBasePath: "/",
- fsBasePath: getPath()
+ fsBasePath: getPath(),
});
const fs = fsInterface(fsAdapter);
const readFile = promisify(fs.readFile);
@@ -84,7 +84,7 @@ const assertStat = async (t, stat, basepath, filepath) => {
test("MemAdapter: stat", async (t) => {
const memAdapter = new MemAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const fs = fsInterface(memAdapter);
const stat = promisify(fs.stat);
@@ -92,7 +92,7 @@ test("MemAdapter: stat", async (t) => {
const fsPath = path.join("/", "foo.txt");
await memAdapter.write(new Resource({
path: "/foo.txt",
- string: `content of ${fsPath}`
+ string: `content of ${fsPath}`,
}));
await assertStat(t, stat, "", fsPath);
});
@@ -100,7 +100,7 @@ test("MemAdapter: stat", async (t) => {
test("FsAdapter: stat", async (t) => {
const fsAdapter = new FsAdapter({
virBasePath: "/",
- fsBasePath: getPath()
+ fsBasePath: getPath(),
});
const fs = fsInterface(fsAdapter);
const stat = promisify(fs.stat);
@@ -114,7 +114,7 @@ test("fs: stat", async (t) => {
test("MemAdapter: mkdir", async (t) => {
const memAdapter = new MemAdapter({
- virBasePath: "/"
+ virBasePath: "/",
});
const fs = fsInterface(memAdapter);
const mkdir = promisify(fs.mkdir);
diff --git a/test/lib/glob.js b/test/lib/glob.ts
similarity index 95%
rename from test/lib/glob.js
rename to test/lib/glob.ts
index 4e8c1b61..40ebdadb 100644
--- a/test/lib/glob.js
+++ b/test/lib/glob.ts
@@ -1,13 +1,13 @@
import test from "ava";
-import FsAdapter from "../../lib/adapters/FileSystem.js";
+import FsAdapter from "../../src/adapters/FileSystem.js";
// Create readerWriter before running tests
test.beforeEach((t) => {
t.context.readerWriter = {
filesystem: new FsAdapter({
fsBasePath: "./test/fixtures/glob",
- virBasePath: "/test-resources/"
- })
+ virBasePath: "/test-resources/",
+ }),
};
});
@@ -18,6 +18,7 @@ function matchGlobResult(t, resources, expectedResources) {
return resource.getPath();
});
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < expectedResources.length; i++) {
const expectedResource = expectedResources[i];
t.true(
@@ -55,7 +56,7 @@ test("glob with virtual base path partially matching", async (t) => {
const adapter = new FsAdapter({
fsBasePath: "./test/fixtures/glob/application.a",
- virBasePath: "/test-resources/application/a/"
+ virBasePath: "/test-resources/application/a/",
});
const resources = await adapter.byGlob("/test-resources/**/*.*");
@@ -106,7 +107,7 @@ test("glob with multiple patterns", async (t) => {
"/test-resources/application.b/webapp/i18n/i18n_de.properties",
"/test-resources/application.b/webapp/embedded/i18n/i18n_de.properties",
"/test-resources/application.b/ui5.yaml",
- "/test-resources/application.a/ui5.yaml"
+ "/test-resources/application.a/ui5.yaml",
];
matchGlobResult(t, resources, expectedResources);
@@ -130,7 +131,7 @@ test("glob two specific filetype (yaml and js)", async (t) => {
const expectedResources = [
"/test-resources/application.a/webapp/test.js",
"/test-resources/application.b/ui5.yaml",
- "/test-resources/application.a/ui5.yaml"
+ "/test-resources/application.a/ui5.yaml",
];
matchGlobResult(t, resources, expectedResources);
@@ -141,7 +142,7 @@ test("glob only a specific filetype (json) with exclude pattern", async (t) => {
const resources = await t.context.readerWriter.filesystem.byGlob([
"/**/*.json",
- "!/**/*package.json"
+ "!/**/*package.json",
]);
resources.forEach((res) => {
@@ -155,7 +156,7 @@ test("glob only a specific filetype (json) with multiple exclude pattern", async
const resources = await t.context.readerWriter.filesystem.byGlob([
"/**/*.json",
"!/**/*package.json",
- "!/**/embedded/manifest.json"
+ "!/**/embedded/manifest.json",
]);
matchGlobResult(t, resources, ["/test-resources/application.b/webapp/manifest.json"]);
@@ -203,14 +204,14 @@ test("glob with multiple patterns with static exclude", async (t) => {
virBasePath: "/test-resources/",
excludes: [
"/test-resources/application.b/**",
- "!/test-resources/application.b/**/manifest.json"
- ]
+ "!/test-resources/application.b/**/manifest.json",
+ ],
}).byGlob(["/**/*.yaml", "/test-resources/**/i18n_de.properties"]);
const expectedResources = [
"/test-resources/application.a/ui5.yaml",
"/test-resources/application.b/webapp/manifest.json",
- "/test-resources/application.b/webapp/embedded/manifest.json"
+ "/test-resources/application.b/webapp/embedded/manifest.json",
];
matchGlobResult(t, resources, expectedResources);
});
@@ -403,4 +404,3 @@ test("glob with multiple patterns with static exclude", async (t) => {
// const matches = await glob([""], opt);
// t.deepEqual(matches.length, 23, "Resources should match");
// });
-
diff --git a/test/lib/package-exports.js b/test/lib/package-exports.ts
similarity index 71%
rename from test/lib/package-exports.js
rename to test/lib/package-exports.ts
index 13201c3d..3214c140 100644
--- a/test/lib/package-exports.js
+++ b/test/lib/package-exports.ts
@@ -19,60 +19,60 @@ test("check number of exports", (t) => {
[
{
exportedSpecifier: "@ui5/fs/adapters/AbstractAdapter",
- mappedModule: "../../lib/adapters/AbstractAdapter.js"
+ mappedModule: "../../src/adapters/AbstractAdapter.js",
},
{
exportedSpecifier: "@ui5/fs/adapters/FileSystem",
- mappedModule: "../../lib/adapters/FileSystem.js"
+ mappedModule: "../../src/adapters/FileSystem.js",
},
{
exportedSpecifier: "@ui5/fs/adapters/Memory",
- mappedModule: "../../lib/adapters/Memory.js"
+ mappedModule: "../../src/adapters/Memory.js",
},
{
exportedSpecifier: "@ui5/fs/AbstractReader",
- mappedModule: "../../lib/AbstractReader.js"
+ mappedModule: "../../src/AbstractReader.js",
},
{
exportedSpecifier: "@ui5/fs/AbstractReaderWriter",
- mappedModule: "../../lib/AbstractReaderWriter.js"
+ mappedModule: "../../src/AbstractReaderWriter.js",
},
{
exportedSpecifier: "@ui5/fs/DuplexCollection",
- mappedModule: "../../lib/DuplexCollection.js"
+ mappedModule: "../../src/DuplexCollection.js",
},
{
exportedSpecifier: "@ui5/fs/fsInterface",
- mappedModule: "../../lib/fsInterface.js"
+ mappedModule: "../../src/fsInterface.js",
},
{
exportedSpecifier: "@ui5/fs/ReaderCollection",
- mappedModule: "../../lib/ReaderCollection.js"
+ mappedModule: "../../src/ReaderCollection.js",
},
{
exportedSpecifier: "@ui5/fs/ReaderCollectionPrioritized",
- mappedModule: "../../lib/ReaderCollectionPrioritized.js"
+ mappedModule: "../../src/ReaderCollectionPrioritized.js",
},
{
exportedSpecifier: "@ui5/fs/readers/Filter",
- mappedModule: "../../lib/readers/Filter.js"
+ mappedModule: "../../src/readers/Filter.js",
},
{
exportedSpecifier: "@ui5/fs/readers/Link",
- mappedModule: "../../lib/readers/Link.js"
+ mappedModule: "../../src/readers/Link.js",
},
{
exportedSpecifier: "@ui5/fs/Resource",
- mappedModule: "../../lib/Resource.js"
+ mappedModule: "../../src/Resource.js",
},
{
exportedSpecifier: "@ui5/fs/resourceFactory",
- mappedModule: "../../lib/resourceFactory.js"
+ mappedModule: "../../src/resourceFactory.js",
},
// Internal modules (only to be used by @ui5/* packages)
{
exportedSpecifier: "@ui5/fs/internal/ResourceTagCollection",
- mappedModule: "../../lib/ResourceTagCollection.js"
+ mappedModule: "../../src/ResourceTagCollection.js",
},
].forEach(({exportedSpecifier, mappedModule}) => {
test(`${exportedSpecifier}`, async (t) => {
diff --git a/test/lib/readers/Filter.js b/test/lib/readers/Filter.ts
similarity index 76%
rename from test/lib/readers/Filter.js
rename to test/lib/readers/Filter.ts
index c48264b4..d9bfb8a7 100644
--- a/test/lib/readers/Filter.js
+++ b/test/lib/readers/Filter.ts
@@ -1,22 +1,22 @@
import test from "ava";
import sinon from "sinon";
-import Filter from "../../../lib/readers/Filter.js";
+import Filter from "../../../src/readers/Filter.js";
test("_byGlob: Basic filter", async (t) => {
const abstractReader = {
- _byGlob: sinon.stub().returns(Promise.resolve(["resource a", "resource b"]))
+ _byGlob: sinon.stub().returns(Promise.resolve(["resource a", "resource b"])),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Filter({
reader: abstractReader,
- callback: function(resource) {
+ callback: function (resource) {
if (resource === "resource a") {
return false;
}
return true;
- }
+ },
});
const resources = await readerCollection._byGlob("anyPattern", {}, trace);
@@ -25,19 +25,19 @@ test("_byGlob: Basic filter", async (t) => {
test("_byPath: Negative filter", async (t) => {
const abstractReader = {
- _byPath: sinon.stub().returns(Promise.resolve("resource a"))
+ _byPath: sinon.stub().returns(Promise.resolve("resource a")),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Filter({
reader: abstractReader,
- callback: function(resource) {
+ callback: function (resource) {
if (resource === "resource a") {
return false;
}
return true;
- }
+ },
});
const resource = await readerCollection._byPath("anyPattern", {}, trace);
@@ -46,19 +46,19 @@ test("_byPath: Negative filter", async (t) => {
test("_byPath: Positive filter", async (t) => {
const abstractReader = {
- _byPath: sinon.stub().returns(Promise.resolve("resource b"))
+ _byPath: sinon.stub().returns(Promise.resolve("resource b")),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Filter({
reader: abstractReader,
- callback: function(resource) {
+ callback: function (resource) {
if (resource === "resource a") {
return false;
}
return true;
- }
+ },
});
const resource = await readerCollection._byPath("anyPattern", {}, trace);
diff --git a/test/lib/readers/Link.js b/test/lib/readers/Link.ts
similarity index 88%
rename from test/lib/readers/Link.js
rename to test/lib/readers/Link.ts
index 01abc5d6..230513bc 100644
--- a/test/lib/readers/Link.js
+++ b/test/lib/readers/Link.ts
@@ -1,27 +1,27 @@
import test from "ava";
import sinon from "sinon";
-import Link from "../../../lib/readers/Link.js";
-import ResourceFacade from "../../../lib/ResourceFacade.js";
+import Link from "../../../src/readers/Link.js";
+import ResourceFacade from "../../../src/ResourceFacade.js";
test("_byGlob: Basic Link", async (t) => {
const dummyResourceA = {
- getPath: () => "/resources/some/lib/FileA.js"
+ getPath: () => "/resources/some/lib/FileA.js",
};
const dummyResourceB = {
- getPath: () => "/resources/some/lib/dir/FileB.js"
+ getPath: () => "/resources/some/lib/dir/FileB.js",
};
const abstractReader = {
- _byGlob: sinon.stub().resolves([dummyResourceA, dummyResourceB])
+ _byGlob: sinon.stub().resolves([dummyResourceA, dummyResourceB]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/`,
- targetPath: `/resources/some/lib/`
- }
+ targetPath: `/resources/some/lib/`,
+ },
});
const options = "options";
const resources = await readerCollection._byGlob("anyPattern", options, trace);
@@ -46,17 +46,17 @@ test("_byGlob: Basic Link", async (t) => {
test("_byGlob: Complex pattern", async (t) => {
const abstractReader = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/`,
- targetPath: `/resources/some/lib/`
- }
+ targetPath: `/resources/some/lib/`,
+ },
});
const options = "options";
@@ -76,17 +76,17 @@ test("_byGlob: Complex pattern", async (t) => {
test("_byGlob: Request prefixed with target path", async (t) => {
const abstractReader = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/my/lib/`,
- targetPath: `/some/lib/`
- }
+ targetPath: `/some/lib/`,
+ },
});
const options = "options";
const resources = await readerCollection._byGlob("/some/lib/dir/**", options, trace);
@@ -104,17 +104,17 @@ test("_byGlob: Request prefixed with target path", async (t) => {
test("_byGlob: Request prefixed with link path", async (t) => {
const abstractReader = {
- _byGlob: sinon.stub().resolves([])
+ _byGlob: sinon.stub().resolves([]),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/my/lib/`,
- targetPath: `/some/lib/`
- }
+ targetPath: `/some/lib/`,
+ },
});
const options = "options";
const resources = await readerCollection._byGlob("/my/lib/dir/**", options, trace);
@@ -132,20 +132,20 @@ test("_byGlob: Request prefixed with link path", async (t) => {
test("_byPath: Basic Link", async (t) => {
const dummyResource = {
- getPath: () => "/resources/some/lib/dir/File.js"
+ getPath: () => "/resources/some/lib/dir/File.js",
};
const abstractReader = {
- _byPath: sinon.stub().resolves(dummyResource)
+ _byPath: sinon.stub().resolves(dummyResource),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/`,
- targetPath: `/resources/some/lib/`
- }
+ targetPath: `/resources/some/lib/`,
+ },
});
const options = "options";
const resource = await readerCollection._byPath("/dir/File.js", options, trace);
@@ -165,20 +165,20 @@ test("_byPath: Basic Link", async (t) => {
test("_byPath: Rewrite on same level", async (t) => {
const dummyResource = {
- getPath: () => "/some/lib/dir/File.js"
+ getPath: () => "/some/lib/dir/File.js",
};
const abstractReader = {
- _byPath: sinon.stub().resolves(dummyResource)
+ _byPath: sinon.stub().resolves(dummyResource),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/my/lib/`,
- targetPath: `/some/lib/`
- }
+ targetPath: `/some/lib/`,
+ },
});
const options = "options";
const resource = await readerCollection._byPath("/my/lib/dir/File.js", options, trace);
@@ -198,17 +198,17 @@ test("_byPath: Rewrite on same level", async (t) => {
test("_byPath: No resource found", async (t) => {
const abstractReader = {
- _byPath: sinon.stub().resolves(null)
+ _byPath: sinon.stub().resolves(null),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/`,
- targetPath: `/some/lib/`
- }
+ targetPath: `/some/lib/`,
+ },
});
const options = "options";
const resource = await readerCollection._byPath("/dir/File.js", options, trace);
@@ -226,17 +226,17 @@ test("_byPath: No resource found", async (t) => {
test("_byPath: Request different prefix", async (t) => {
const abstractReader = {
- _byPath: sinon.stub()
+ _byPath: sinon.stub(),
};
const trace = {
- collection: sinon.spy()
+ collection: sinon.spy(),
};
const readerCollection = new Link({
reader: abstractReader,
pathMapping: {
linkPath: `/my/lib/`,
- targetPath: `/some/lib/`
- }
+ targetPath: `/some/lib/`,
+ },
});
const options = "options";
const resource = await readerCollection._byPath("/some/lib/dir/File.js", options, trace);
@@ -252,7 +252,7 @@ test("Missing attributes", (t) => {
pathMapping: {
linkPath: `/`,
targetPath: `/`,
- }
+ },
});
});
t.is(err.message, `Missing parameter "reader"`,
@@ -260,7 +260,7 @@ test("Missing attributes", (t) => {
err = t.throws(() => {
new Link({
- reader: abstractReader
+ reader: abstractReader,
});
});
t.is(err.message, `Missing parameter "pathMapping"`,
@@ -271,7 +271,7 @@ test("Missing attributes", (t) => {
reader: abstractReader,
pathMapping: {
targetPath: `/`,
- }
+ },
});
});
t.is(err.message, `Path mapping is missing attribute "linkPath"`,
@@ -282,7 +282,7 @@ test("Missing attributes", (t) => {
reader: abstractReader,
pathMapping: {
linkPath: `/`,
- }
+ },
});
});
t.is(err.message, `Path mapping is missing attribute "targetPath"`,
@@ -294,7 +294,7 @@ test("Missing attributes", (t) => {
pathMapping: {
linkPath: `/path`,
targetPath: `/`,
- }
+ },
});
});
t.is(err.message, `Link path must end with a slash: /path`,
@@ -306,7 +306,7 @@ test("Missing attributes", (t) => {
pathMapping: {
linkPath: `/`,
targetPath: `/path`,
- }
+ },
});
});
t.is(err.message, `Target path must end with a slash: /path`,
diff --git a/test/lib/resourceFactory.js b/test/lib/resourceFactory.ts
similarity index 83%
rename from test/lib/resourceFactory.js
rename to test/lib/resourceFactory.ts
index c535ff90..27c2d0ab 100644
--- a/test/lib/resourceFactory.js
+++ b/test/lib/resourceFactory.ts
@@ -1,10 +1,10 @@
import test from "ava";
import {
createAdapter, createReader, createReaderCollection, createReaderCollectionPrioritized,
- createResource, createWriterCollection, createWorkspace, prefixGlobPattern} from "../../lib/resourceFactory.js";
-import FileSystem from "../../lib/adapters/FileSystem.js";
-import Memory from "../../lib/adapters/Memory.js";
-import ReaderCollection from "../../lib/ReaderCollection.js";
+ createResource, createWriterCollection, createWorkspace, prefixGlobPattern} from "../../src/resourceFactory.js";
+import FileSystem from "../../src/adapters/FileSystem.js";
+import Memory from "../../src/adapters/Memory.js";
+import ReaderCollection from "../../src/ReaderCollection.js";
import {setLogLevel} from "@ui5/logger";
// Set log level to silly to activate tracing
@@ -15,7 +15,7 @@ test("prefixGlobPattern", (t) => {
prefixGlobPattern("{/sub-directory-1/,/sub-directory-2/}**", "/pony/path/a"),
[
"/pony/path/a/sub-directory-1/**",
- "/pony/path/a/sub-directory-2/**"
+ "/pony/path/a/sub-directory-2/**",
],
"GLOBs correctly prefixed");
@@ -50,9 +50,9 @@ test("createAdapter: FS Adapter", async (t) => {
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
+ getName: () => "my.project",
},
- excludes: ["**/*.html"]
+ excludes: ["**/*.html"],
});
t.true(adapter instanceof FileSystem, "Returned a FileSystem adapter");
@@ -66,20 +66,20 @@ test("createAdapter: Memory", async (t) => {
const adapter = createAdapter({
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
+ getName: () => "my.project",
},
- excludes: ["**/*.html"]
+ excludes: ["**/*.html"],
});
t.true(adapter instanceof Memory, "Returned a Memory adapter");
const resource1 = createResource({
- path: "/resources/app/File.js"
+ path: "/resources/app/File.js",
});
await adapter.write(resource1);
const resource2 = createResource({
- path: "/resources/app/index.html"
+ path: "/resources/app/index.html",
});
await adapter.write(resource2);
@@ -94,7 +94,7 @@ test("createReader: application project", async (t) => {
virBasePath: "/resources/app/",
project: {
getName: () => "my.project",
- getType: () => "application"
+ getType: () => "application",
},
excludes: [
"**/*.html",
@@ -102,9 +102,9 @@ test("createReader: application project", async (t) => {
"/test/**",
"test/**",
"!/resources/app/test/**",
- "!/test/**/*.html"
+ "!/test/**/*.html",
],
- name: "reader name"
+ name: "reader name",
});
t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection");
@@ -130,16 +130,16 @@ test("createReader: library project", async (t) => {
virBasePath: "/resources/lib/",
project: {
getName: () => "my.project",
- getType: () => "library"
+ getType: () => "library",
},
excludes: [
"**/*.html",
"/resources/lib/dir/**",
"/test-resources/lib/dir/**",
"/test/**",
- "test/**"
+ "test/**",
],
- name: "reader name"
+ name: "reader name",
});
t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection");
@@ -167,9 +167,9 @@ test("createReader: No project", async (t) => {
"/resources/app/dir/**",
"/test-resources/app/dir/**",
"/test/**",
- "test/**"
+ "test/**",
],
- name: "reader name"
+ name: "reader name",
});
t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection");
@@ -184,7 +184,7 @@ test("createReader: No project", async (t) => {
"/resources/app/dir/**",
"/test-resources/app/dir/**",
"/test/**",
- "test/**"
+ "test/**",
], "Excludes do not get prefixed.");
});
@@ -192,10 +192,10 @@ test("createReader: Throw error missing 'fsBasePath'", (t) => {
const error = t.throws(() => createReader({
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
+ getName: () => "my.project",
},
excludes: ["**/*.html"],
- name: "reader name"
+ name: "reader name",
}));
t.is(error.message, "Unable to create reader: Missing parameter \"fsBasePath\"");
});
@@ -204,22 +204,22 @@ test("createReaderCollection", async (t) => {
const adapter = createAdapter({
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
+ getName: () => "my.project",
},
- excludes: ["**/*.html"]
+ excludes: ["**/*.html"],
});
const resource1 = createResource({
- path: "/resources/app/File.js"
+ path: "/resources/app/File.js",
});
const resource2 = createResource({
- path: "/resources/app/index.html"
+ path: "/resources/app/index.html",
});
await adapter.write(resource1);
await adapter.write(resource2);
const reader = createReaderCollection({
name: "reader name",
- readers: [adapter]
+ readers: [adapter],
});
t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection");
t.is(reader._name, "reader name", "Reader has correct name");
@@ -230,26 +230,26 @@ test("createReaderCollection", async (t) => {
});
test("createReaderCollectionPrioritized", async (t) => {
- const {default: ReaderCollectionPrioritized} = await import("../../lib/ReaderCollectionPrioritized.js");
+ const {default: ReaderCollectionPrioritized} = await import("../../src/ReaderCollectionPrioritized.js");
const adapter = createAdapter({
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
+ getName: () => "my.project",
},
- excludes: ["**/*.html"]
+ excludes: ["**/*.html"],
});
const resource1 = createResource({
- path: "/resources/app/File.js"
+ path: "/resources/app/File.js",
});
const resource2 = createResource({
- path: "/resources/app/index.html"
+ path: "/resources/app/index.html",
});
await adapter.write(resource1);
await adapter.write(resource2);
const reader = createReaderCollectionPrioritized({
name: "reader name",
- readers: [adapter]
+ readers: [adapter],
});
t.true(reader instanceof ReaderCollectionPrioritized, "Returned a ReaderCollection");
t.is(reader._name, "reader name", "Reader has correct name");
@@ -260,32 +260,32 @@ test("createReaderCollectionPrioritized", async (t) => {
});
test("createWriterCollection", async (t) => {
- const {default: WriterCollection} = await import("../../lib/WriterCollection.js");
+ const {default: WriterCollection} = await import("../../src/WriterCollection.js");
const adapter1 = createAdapter({
virBasePath: "/",
project: {
- getName: () => "my.project"
- }
+ getName: () => "my.project",
+ },
});
const adapter2 = createAdapter({
virBasePath: "/",
project: {
- getName: () => "my.other.project"
- }
+ getName: () => "my.other.project",
+ },
});
const resource1 = createResource({
- path: "/resources/app/File.js"
+ path: "/resources/app/File.js",
});
const resource2 = createResource({
- path: "/resources/app2/index.html"
+ path: "/resources/app2/index.html",
});
const writerCollection = createWriterCollection({
name: "writer collection name",
writerMapping: {
"/resources/app/": adapter1,
- "/resources/app2/": adapter2
- }
+ "/resources/app2/": adapter2,
+ },
});
t.true(writerCollection instanceof WriterCollection, "Returned a ReaderCollection");
await writerCollection.write(resource1);
@@ -300,30 +300,30 @@ test("createWriterCollection", async (t) => {
});
test("createWorkspace", async (t) => {
- const {default: DuplexCollection} = await import("../../lib/DuplexCollection.js");
+ const {default: DuplexCollection} = await import("../../src/DuplexCollection.js");
const reader = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
- }
+ getName: () => "my.project",
+ },
});
const readerWriter = createAdapter({
virBasePath: "/",
project: {
- getName: () => "my.other.project"
- }
+ getName: () => "my.other.project",
+ },
});
const writerCollection = createWorkspace({
name: "writer collection name",
reader,
- writer: readerWriter
+ writer: readerWriter,
});
t.true(writerCollection instanceof DuplexCollection, "Returned a ReaderCollection");
const resource1 = createResource({
- path: "/resources/app/File.js"
+ path: "/resources/app/File.js",
});
await writerCollection.write(resource1);
@@ -333,20 +333,19 @@ test("createWorkspace", async (t) => {
});
test("createWorkspace: Without writer", async (t) => {
- const {default: DuplexCollection} = await import("../../lib/DuplexCollection.js");
- const {default: Memory} = await import("../../lib/adapters/Memory.js");
+ const {default: DuplexCollection} = await import("../../src/DuplexCollection.js");
+ const {default: Memory} = await import("../../src/adapters/Memory.js");
const reader = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
virBasePath: "/resources/app/",
project: {
- getName: () => "my.project"
- }
+ getName: () => "my.project",
+ },
});
const writerCollection = createWorkspace({
name: "writer collection name",
- reader
+ reader,
});
t.true(writerCollection instanceof DuplexCollection, "Returned a ReaderCollection");
t.true(writerCollection._writer instanceof Memory, "Internal Writer is created and a MemAdapter");
});
-
diff --git a/test/lib/resources.js b/test/lib/resources.ts
similarity index 64%
rename from test/lib/resources.js
rename to test/lib/resources.ts
index 64b861c5..35b5ae77 100644
--- a/test/lib/resources.js
+++ b/test/lib/resources.ts
@@ -2,25 +2,35 @@ import test from "ava";
import sinon from "sinon";
import {readFile} from "node:fs/promises";
+// const test = anyTest as TestFn<{
+// // buildLogger: BuildLogger;
+// // logStub: sinon.SinonStub;
+// // logHandler: sinon.SinonStub;
+// // metadataHandler: sinon.SinonStub;
+// // statusHandler: sinon.SinonStub;
+// }>;
+
import {createAdapter, createFilterReader,
- createFlatReader, createLinkReader, createResource} from "../../lib/resourceFactory.js";
+ createFlatReader, createLinkReader, createResource} from "../../src/resourceFactory.js";
+import {type Project} from "@ui5/project/specifications/Project";
-test.afterEach.always((t) => {
+test.afterEach.always(() => {
sinon.restore();
});
-function getFileContent(path) {
+function getFileContent(path: string) {
return readFile(path, "utf8");
}
-async function fileEqual(t, actual, expected) {
+async function fileEqual(t, actual: string, expected: string) {
const actualContent = await getFileContent(actual);
const expectedContent = await getFileContent(expected);
t.is(actualContent, expectedContent);
}
["FileSystem", "Memory"].forEach((adapter) => {
- async function getAdapter(config) {
+ async function getAdapter(config:
+ {fsBasePath?: string; virBasePath: string; project?: Project; excludes?: string[]; useGitignore?: boolean}) {
if (adapter === "Memory") {
const fsAdapter = createAdapter(config);
const fsResources = await fsAdapter.byGlob("**/*");
@@ -40,22 +50,22 @@ async function fileEqual(t, actual, expected) {
Always make sure that every test writes to a separate file! By default, tests are running concurrent.
*/
test(adapter +
- ": Get resource from application.a (/index.html) and write it to /dest/ using a ReadableStream", async (t) => {
+ ": Get resource from application.a (/index.html) and write it to /dest/ using a ReadableStream", async (t) => {
const source = await getAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const dest = await getAdapter({
fsBasePath: "./test/tmp/readerWriters/application.a/simple-read-write",
- virBasePath: "/dest/"
+ virBasePath: "/dest/",
});
// Get resource from one readerWriter
- const resource = await source.byPath("/app/index.html");
+ const resource = await source!.byPath("/app/index.html");
// Write resource content to another readerWriter
- resource.setPath("/dest/index_readableStreamTest.html");
- await dest.write(resource);
+ resource!.setPath("/dest/index_readableStreamTest.html");
+ await dest!.write(resource);
t.notThrows(async () => {
if (adapter === "FileSystem") {
@@ -64,8 +74,8 @@ async function fileEqual(t, actual, expected) {
"./test/tmp/readerWriters/application.a/simple-read-write/index_readableStreamTest.html",
"./test/fixtures/application.a/webapp/index.html");
} else {
- const destResource = await dest.byPath("/dest/index_readableStreamTest.html");
- t.deepEqual(await destResource.getString(), await resource.getString());
+ const destResource = await dest!.byPath("/dest/index_readableStreamTest.html");
+ t.deepEqual(await destResource!.getString(), await resource!.getString());
}
});
});
@@ -73,83 +83,83 @@ async function fileEqual(t, actual, expected) {
test(adapter + ": Create resource, write and change content", async (t) => {
const dest = await getAdapter({
fsBasePath: "./test/tmp/writer/",
- virBasePath: "/dest/writer/"
+ virBasePath: "/dest/writer/",
});
const resource = createResource({
path: "/dest/writer/content/test.js",
- string: "MyInitialContent"
+ string: "MyInitialContent",
});
- await dest.write(resource);
+ await dest!.write(resource);
resource.setString("MyNewContent");
- const resource1 = await dest.byPath("/dest/writer/content/test.js");
+ const resource1 = await dest!.byPath("/dest/writer/content/test.js");
t.is(await resource.getString(), "MyNewContent");
- t.is(await resource1.getString(), "MyInitialContent");
+ t.is(await resource1!.getString(), "MyInitialContent");
t.is(await resource.getString(), "MyNewContent");
- t.is(await resource1.getString(), "MyInitialContent");
+ t.is(await resource1!.getString(), "MyInitialContent");
- await dest.write(resource);
+ await dest!.write(resource);
- const resource2 = await dest.byPath("/dest/writer/content/test.js");
+ const resource2 = await dest!.byPath("/dest/writer/content/test.js");
t.is(await resource.getString(), "MyNewContent");
- t.is(await resource2.getString(), "MyNewContent");
+ t.is(await resource2!.getString(), "MyNewContent");
});
test(adapter + ": Create resource, write and change path", async (t) => {
const dest = await getAdapter({
fsBasePath: "./test/tmp/writer/",
- virBasePath: "/dest/writer/"
+ virBasePath: "/dest/writer/",
});
const resource = createResource({
path: "/dest/writer/path/test.js",
- string: "MyInitialContent"
+ string: "MyInitialContent",
});
- await dest.write(resource);
+ await dest!.write(resource);
resource.setPath("/dest/writer/path/test2.js");
- const resourceOldPath = await dest.byPath("/dest/writer/path/test.js");
- const resourceNewPath = await dest.byPath("/dest/writer/path/test2.js");
+ const resourceOldPath = await dest!.byPath("/dest/writer/path/test.js");
+ const resourceNewPath = await dest!.byPath("/dest/writer/path/test2.js");
- t.is(await resource.getPath(), "/dest/writer/path/test2.js");
+ t.is(resource.getPath(), "/dest/writer/path/test2.js");
t.truthy(resourceOldPath);
- t.is(await resourceOldPath.getString(), await resource.getString());
- t.is(await resourceOldPath.getPath(), "/dest/writer/path/test.js");
- t.not(resourceNewPath);
+ t.is(await resourceOldPath!.getString(), await resource.getString());
+ t.is(resourceOldPath!.getPath(), "/dest/writer/path/test.js");
+ t.not(resourceNewPath, undefined);
- await dest.write(resource);
+ await dest!.write(resource);
- const resourceOldPath1 = await dest.byPath("/dest/writer/path/test.js");
- const resourceNewPath1 = await dest.byPath("/dest/writer/path/test2.js");
+ const resourceOldPath1 = await dest!.byPath("/dest/writer/path/test.js");
+ const resourceNewPath1 = await dest!.byPath("/dest/writer/path/test2.js");
- t.is(await resource.getPath(), "/dest/writer/path/test2.js");
+ t.is(resource.getPath(), "/dest/writer/path/test2.js");
t.truthy(resourceNewPath1);
- t.is(await resourceNewPath1.getString(), await resource.getString());
- t.is(await resourceNewPath1.getPath(), "/dest/writer/path/test2.js");
- t.not(resourceOldPath1);
+ t.is(await resourceNewPath1!.getString(), await resource.getString());
+ t.is(resourceNewPath1!.getPath(), "/dest/writer/path/test2.js");
+ t.not(resourceOldPath1, undefined);
});
test(adapter +
- ": Create a resource with a path different from the path configured in the adapter", async (t) => {
- t.pass(2);
+ ": Create a resource with a path different from the path configured in the adapter", async (t) => {
+ t.pass("2");
const dest = await getAdapter({
fsBasePath: "./test/tmp/writer/",
- virBasePath: "/dest2/writer/"
+ virBasePath: "/dest2/writer/",
});
const resource = createResource({
path: "/dest2/tmp/test.js",
- string: "MyContent"
+ string: "MyContent",
});
- const error = await t.throwsAsync(dest.write(resource));
+ const error = await t.throwsAsync(dest!.write(resource));
t.is(error.message,
"Failed to write resource with virtual path '/dest2/tmp/test.js': Path must start with the " +
"configured virtual base path of the adapter. Base path: '/dest2/writer/'",
@@ -157,16 +167,16 @@ async function fileEqual(t, actual, expected) {
});
test(adapter +
- ": Create a resource with a path above the path configured in the adapter", async (t) => {
- t.pass(2);
+ ": Create a resource with a path above the path configured in the adapter", async (t) => {
+ t.pass("2");
const dest = await getAdapter({
fsBasePath: "./test/tmp/writer/",
- virBasePath: "/dest2/writer/"
+ virBasePath: "/dest2/writer/",
});
const resource = createResource({
path: "/dest2/test.js",
- string: "MyContent"
+ string: "MyContent",
});
const error = await t.throwsAsync(dest.write(resource));
@@ -177,16 +187,16 @@ async function fileEqual(t, actual, expected) {
});
test(adapter +
- ": Create a resource with a path resolving outside the path configured in the adapter", async (t) => {
- t.pass(2);
+ ": Create a resource with a path resolving outside the path configured in the adapter", async (t) => {
+ t.pass("2");
const dest = await getAdapter({
fsBasePath: "./test/tmp/writer/",
- virBasePath: "/dest/writer/"
+ virBasePath: "/dest/writer/",
});
const resource = createResource({
path: "/dest/writer/../relative.js",
- string: "MyContent"
+ string: "MyContent",
});
// Resource will already resolve relative path segments
t.is(resource.getPath(), "/dest/relative.js", "Resource path resolved");
@@ -198,20 +208,20 @@ async function fileEqual(t, actual, expected) {
await t.throwsAsync(dest.write(resource), {
message:
"Failed to write resource with virtual path '/dest/writer/../relative.js': " +
- "Path must start with the configured virtual base path of the adapter. Base path: '/dest/writer/'"
+ "Path must start with the configured virtual base path of the adapter. Base path: '/dest/writer/'",
}, "Threw with expected error message");
});
test(adapter + ": Filter resources", async (t) => {
const source = createAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/app/"
+ virBasePath: "/app/",
});
const filteredSource = createFilterReader({
reader: source,
callback: (resource) => {
return resource.getPath().endsWith(".js");
- }
+ },
});
const sourceResources = await source.byGlob("**");
t.is(sourceResources.length, 2, "Found two resources in source");
@@ -225,11 +235,11 @@ async function fileEqual(t, actual, expected) {
test(adapter + ": Flatten resources", async (t) => {
const source = await getAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const transformedSource = createFlatReader({
- reader: source,
- namespace: "app"
+ reader: source!,
+ namespace: "app",
});
const resources = await transformedSource.byGlob("**/*.js");
@@ -240,14 +250,14 @@ async function fileEqual(t, actual, expected) {
test(adapter + ": Link resources", async (t) => {
const source = await getAdapter({
fsBasePath: "./test/fixtures/application.a/webapp",
- virBasePath: "/resources/app/"
+ virBasePath: "/resources/app/",
});
const transformedSource = createLinkReader({
- reader: source,
+ reader: source!,
pathMapping: {
linkPath: "/wow/this/is/a/beautiful/path/just/wow/",
- targetPath: "/resources/"
- }
+ targetPath: "/resources/",
+ },
});
const resources = await transformedSource.byGlob("**/*.js");
diff --git a/test/lib/tracing/traceSummary.js b/test/lib/tracing/traceSummary.ts
similarity index 78%
rename from test/lib/tracing/traceSummary.js
rename to test/lib/tracing/traceSummary.ts
index 94bbd746..96428a40 100644
--- a/test/lib/tracing/traceSummary.js
+++ b/test/lib/tracing/traceSummary.ts
@@ -1,23 +1,32 @@
-import test from "ava";
-import sinon from "sinon";
-import esmock from "esmock";
+import anyTest, {type TestFn, type ExecutionContext} from "ava";
+import sinon, {type SinonStub} from "sinon";
+import esmock, {type MockFunction} from "esmock";
+
+interface avaContext {
+ loggerStub: {
+ silly: SinonStub;
+ isLevelEnabled: (arg: boolean) => boolean;
+ };
+ traceSummary: MockFunction;
+}
+const test = anyTest as TestFn;
-async function createMock(t, isLevelEnabled=true) {
+async function createMock(t: ExecutionContext, isLevelEnabled = true) {
t.context.loggerStub = {
silly: sinon.stub(),
isLevelEnabled: () => {
return isLevelEnabled;
- }
+ },
};
- t.context.traceSummary = await esmock("../../../lib/tracing/traceSummary.js", {
+ t.context.traceSummary = await esmock("../../../src/tracing/traceSummary.js", {
"@ui5/logger": {
- getLogger: sinon.stub().returns(t.context.loggerStub)
- }
+ getLogger: sinon.stub().returns(t.context.loggerStub),
+ },
});
return t.context;
}
-test.afterEach.always((t) => {
+test.afterEach.always((_t) => {
sinon.restore();
});
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 00000000..02c658af
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "outDir": "./lib",
+ "moduleResolution": "node16",
+ "module": "node16",
+ "target": "es2022",
+ "lib": ["ES2022"],
+ "strict": true,
+ "sourceMap": true,
+ "declaration": true,
+ "declarationMap": true,
+ }
+}
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 00000000..0aa09f59
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,5 @@
+{
+ // For creating a release build, only compile the src dir
+ "extends": "./tsconfig.base.json",
+ "include": ["src/**/*"],
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..2f4ae369
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ // This is our configuration for development and linting,
+ // compared to "tsconfig.build.json" it also includes tests
+ // and other TypeScript sources in the project
+ // For this reason however, it should not be used to emit JavaScript (except for linting purposes),
+ // since the output directory would then contain "src" and "test" directories instead of just the content of "src"
+ "extends": "./tsconfig.base.json",
+ "include": [
+ "src/**/*",
+ "test/**/*",
+ ],
+ "exclude": [
+ "test/tmp/**/*",
+ "test/fixtures/**/*",
+ ]
+}
diff --git a/typedoc.config.js b/typedoc.config.js
new file mode 100644
index 00000000..080f4c02
--- /dev/null
+++ b/typedoc.config.js
@@ -0,0 +1,10 @@
+/** @type {Partial} */
+const config = {
+ entryPoints: ["./src/"],
+ tsconfig: "tsconfig.build.json",
+ out: "jsdocs",
+ entryPointStrategy: "expand",
+ plugin: ["typedoc-plugin-rename-defaults"],
+};
+
+export default config;