mirror of https://github.com/webpack/webpack.git
				
				
				
			feat: url assets
This commit is contained in:
		
							parent
							
								
									3e4c2ef87a
								
							
						
					
					
						commit
						ddc83b0d70
					
				|  | @ -1011,6 +1011,10 @@ export interface RuleSetRule { | |||
| 	 * Match the child compiler name. | ||||
| 	 */ | ||||
| 	compiler?: RuleSetConditionOrConditions; | ||||
| 	/** | ||||
| 	 * Match dependency type. | ||||
| 	 */ | ||||
| 	dependency?: string; | ||||
| 	/** | ||||
| 	 * Match values of properties in the description file (usually package.json). | ||||
| 	 */ | ||||
|  |  | |||
|  | @ -0,0 +1,53 @@ | |||
| /* | ||||
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||||
| 	Author Ivan Kopeykin @vankop | ||||
| */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const BaseURIRuntimeModule = require("./BaseURIRuntimeModule"); | ||||
| const RuntimeGlobals = require("./RuntimeGlobals"); | ||||
| 
 | ||||
| /** @typedef {import("../declarations/WebpackOptions").Target} Target */ | ||||
| /** @typedef {import("./Compiler")} Compiler */ | ||||
| 
 | ||||
| class BaseURIPlugin { | ||||
| 	/** | ||||
| 	 * @param {Target} target target | ||||
| 	 */ | ||||
| 	constructor(target) { | ||||
| 		switch (target) { | ||||
| 			case "webworker": | ||||
| 				this.environment = /** @type {"webworker"} */ ("webworker"); | ||||
| 				break; | ||||
| 			case "node": | ||||
| 			case "async-node": | ||||
| 			case "node-webkit": | ||||
| 			case "electron-main": | ||||
| 			case "electron-preload": | ||||
| 				this.environment = /** @type {"node"} */ ("node"); | ||||
| 				break; | ||||
| 			default: | ||||
| 				this.environment = /** @type {"web"} */ ("web"); | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * @param {Compiler} compiler compiler | ||||
| 	 */ | ||||
| 	apply(compiler) { | ||||
| 		compiler.hooks.compilation.tap("BaseURIPlugin", compilation => { | ||||
| 			compilation.hooks.runtimeRequirementInTree | ||||
| 				.for(RuntimeGlobals.baseURI) | ||||
| 				.tap("BaseURIPlugin", (chunk, set) => { | ||||
| 					compilation.addRuntimeModule( | ||||
| 						chunk, | ||||
| 						new BaseURIRuntimeModule(this.environment) | ||||
| 					); | ||||
| 				}); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = BaseURIPlugin; | ||||
|  | @ -0,0 +1,44 @@ | |||
| /* | ||||
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||||
| 	Author Ivan Kopeykin @vankop | ||||
| */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const RuntimeGlobals = require("./RuntimeGlobals"); | ||||
| const RuntimeModule = require("./RuntimeModule"); | ||||
| const Template = require("./Template"); | ||||
| 
 | ||||
| class BaseURIRuntimeModule extends RuntimeModule { | ||||
| 	/** | ||||
| 	 * @param {"node"|"web"|"webworker"} environment environment | ||||
| 	 */ | ||||
| 	constructor(environment) { | ||||
| 		super("baseURI"); | ||||
| 		this.env = environment; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * @returns {string} runtime code | ||||
| 	 */ | ||||
| 	generate() { | ||||
| 		switch (this.env) { | ||||
| 			case "web": | ||||
| 				return Template.asString([ | ||||
| 					`${RuntimeGlobals.baseURI} = document.baseURI;` | ||||
| 				]); | ||||
| 			case "webworker": | ||||
| 				return Template.asString([ | ||||
| 					`${RuntimeGlobals.baseURI} = self.location;` | ||||
| 				]); | ||||
| 			case "node": | ||||
| 				return Template.asString([ | ||||
| 					`${RuntimeGlobals.baseURI} = require('url').pathToFileURL(__filename);` | ||||
| 				]); | ||||
| 			default: | ||||
| 				return ""; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = BaseURIRuntimeModule; | ||||
|  | @ -132,6 +132,7 @@ const dependencyCache = new WeakMap(); | |||
| const ruleSetCompiler = new RuleSetCompiler([ | ||||
| 	new BasicMatcherRulePlugin("test", "resource"), | ||||
| 	new BasicMatcherRulePlugin("mimetype"), | ||||
| 	new BasicMatcherRulePlugin("dependency"), | ||||
| 	new BasicMatcherRulePlugin("include", "resource"), | ||||
| 	new BasicMatcherRulePlugin("exclude", "resource", true), | ||||
| 	new BasicMatcherRulePlugin("resource"), | ||||
|  |  | |||
|  | @ -278,3 +278,8 @@ exports.hasOwnProperty = "__webpack_require__.o"; | |||
|  * the System.register context object | ||||
|  */ | ||||
| exports.systemContext = "__webpack_require__.y"; | ||||
| 
 | ||||
| /** | ||||
|  * the baseURI of current document | ||||
|  */ | ||||
| exports.baseURI = "__webpack_require__.b"; | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ const TemplatedPathPlugin = require("./TemplatedPathPlugin"); | |||
| const UseStrictPlugin = require("./UseStrictPlugin"); | ||||
| const WarnCaseSensitiveModulesPlugin = require("./WarnCaseSensitiveModulesPlugin"); | ||||
| 
 | ||||
| const BaseURIPlugin = require("./BaseURIPlugin"); | ||||
| const URLPlugin = require("./dependencies/URLPlugin"); | ||||
| const DataUriPlugin = require("./schemes/DataUriPlugin"); | ||||
| const FileUriPlugin = require("./schemes/FileUriPlugin"); | ||||
| 
 | ||||
|  | @ -313,6 +315,8 @@ class WebpackOptionsApply extends OptionsApply { | |||
| 				: true | ||||
| 		}).apply(compiler); | ||||
| 
 | ||||
| 		new BaseURIPlugin(options.target).apply(compiler); | ||||
| 		new URLPlugin().apply(compiler); | ||||
| 		new DataUriPlugin().apply(compiler); | ||||
| 		new FileUriPlugin().apply(compiler); | ||||
| 
 | ||||
|  |  | |||
|  | @ -356,6 +356,13 @@ const applyModuleDefaults = ( | |||
| 				mimetype: "application/node", | ||||
| 				type: "javascript/auto" | ||||
| 			}, | ||||
| 			{ | ||||
| 				test: /\.js$/, | ||||
| 				dependency: "url", | ||||
| 				resolve: { | ||||
| 					conditionNames: ["esm", "..."] | ||||
| 				} | ||||
| 			}, | ||||
| 			{ | ||||
| 				test: /\.json$/i, | ||||
| 				type: "json" | ||||
|  |  | |||
|  | @ -0,0 +1,75 @@ | |||
| /* | ||||
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||||
| 	Author Ivan Kopeykin @vankop | ||||
| */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const RuntimeGlobals = require("../RuntimeGlobals"); | ||||
| const ModuleDependency = require("./ModuleDependency"); | ||||
| 
 | ||||
| /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */ | ||||
| /** @typedef {import("../ChunkGraph")} ChunkGraph */ | ||||
| /** @typedef {import("../Dependency")} Dependency */ | ||||
| /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */ | ||||
| /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */ | ||||
| /** @typedef {import("../ModuleGraph")} ModuleGraph */ | ||||
| /** @typedef {import("../util/Hash")} Hash */ | ||||
| 
 | ||||
| class URLDependency extends ModuleDependency { | ||||
| 	/** | ||||
| 	 * @param {string} request request | ||||
| 	 * @param {[number, number]} range range | ||||
| 	 */ | ||||
| 	constructor(request, range) { | ||||
| 		super(request); | ||||
| 		this.range = range; | ||||
| 	} | ||||
| 
 | ||||
| 	get type() { | ||||
| 		return "new URL()"; | ||||
| 	} | ||||
| 
 | ||||
| 	get category() { | ||||
| 		return "url"; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| URLDependency.Template = class URLDependencyTemplate extends ModuleDependency.Template { | ||||
| 	/** | ||||
| 	 * @param {Dependency} dependency the dependency for which the template should be applied | ||||
| 	 * @param {ReplaceSource} source the current replace source which can be modified | ||||
| 	 * @param {DependencyTemplateContext} templateContext the context object | ||||
| 	 * @returns {void} | ||||
| 	 */ | ||||
| 	apply(dependency, source, templateContext) { | ||||
| 		const { | ||||
| 			chunkGraph, | ||||
| 			moduleGraph, | ||||
| 			runtimeRequirements, | ||||
| 			runtimeTemplate, | ||||
| 			runtime | ||||
| 		} = templateContext; | ||||
| 		const dep = /** @type {URLDependency} */ (dependency); | ||||
| 
 | ||||
| 		const connection = moduleGraph.getConnection(dep); | ||||
| 		if (connection && !connection.isActive(runtime)) return; | ||||
| 
 | ||||
| 		runtimeRequirements.add(RuntimeGlobals.baseURI); | ||||
| 		runtimeRequirements.add(RuntimeGlobals.require); | ||||
| 
 | ||||
| 		source.replace( | ||||
| 			dep.range[0], | ||||
| 			dep.range[1] - 1, | ||||
| 			`new URL(/* asset import */ ${runtimeTemplate.moduleRaw({ | ||||
| 				chunkGraph, | ||||
| 				module: moduleGraph.getModule(dep), | ||||
| 				request: dep.request, | ||||
| 				runtimeRequirements, | ||||
| 				weak: false | ||||
| 			})}, ${RuntimeGlobals.baseURI})` | ||||
| 		); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| module.exports = URLDependency; | ||||
|  | @ -0,0 +1,78 @@ | |||
| /* | ||||
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | ||||
| 	Author Ivan Kopeykin @vankop | ||||
| */ | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const URLDependency = require("./URLDependency"); | ||||
| 
 | ||||
| /** @typedef {import("estree").NewExpression} NewExpressionNode */ | ||||
| /** @typedef {import("../Compiler")} Compiler */ | ||||
| /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */ | ||||
| 
 | ||||
| class URLPlugin { | ||||
| 	/** | ||||
| 	 * @param {Compiler} compiler compiler | ||||
| 	 */ | ||||
| 	apply(compiler) { | ||||
| 		compiler.hooks.compilation.tap( | ||||
| 			"URLPlugin", | ||||
| 			(compilation, { normalModuleFactory }) => { | ||||
| 				compilation.dependencyFactories.set(URLDependency, normalModuleFactory); | ||||
| 				compilation.dependencyTemplates.set( | ||||
| 					URLDependency, | ||||
| 					new URLDependency.Template() | ||||
| 				); | ||||
| 
 | ||||
| 				/** | ||||
| 				 * @param {JavascriptParser} parser parser | ||||
| 				 */ | ||||
| 				const parserCallback = parser => { | ||||
| 					parser.hooks.new.for("URL").tap("URLPlugin", _expr => { | ||||
| 						const expr = /** @type {NewExpressionNode} */ (_expr); | ||||
| 
 | ||||
| 						if (expr.arguments.length !== 2) return; | ||||
| 
 | ||||
| 						const [arg1, arg2] = expr.arguments; | ||||
| 
 | ||||
| 						if ( | ||||
| 							arg2.type !== "MemberExpression" || | ||||
| 							arg1.type === "SpreadElement" | ||||
| 						) | ||||
| 							return; | ||||
| 
 | ||||
| 						const chain = parser.extractMemberExpressionChain(arg2); | ||||
| 
 | ||||
| 						if ( | ||||
| 							chain.members.length !== 1 || | ||||
| 							chain.object.type !== "MetaProperty" || | ||||
| 							chain.object.property.name !== "meta" || | ||||
| 							chain.members[0] !== "url" | ||||
| 						) | ||||
| 							return; | ||||
| 
 | ||||
| 						const request = parser.evaluateExpression(arg1).asString(); | ||||
| 
 | ||||
| 						if (!request) return; | ||||
| 
 | ||||
| 						const dep = new URLDependency(request, expr.range); | ||||
| 						dep.loc = expr.loc; | ||||
| 						parser.state.module.addDependency(dep); | ||||
| 						return true; | ||||
| 					}); | ||||
| 				}; | ||||
| 
 | ||||
| 				normalModuleFactory.hooks.parser | ||||
| 					.for("javascript/auto") | ||||
| 					.tap("URLPlugin", parserCallback); | ||||
| 
 | ||||
| 				normalModuleFactory.hooks.parser | ||||
| 					.for("javascript/esm") | ||||
| 					.tap("URLPlugin", parserCallback); | ||||
| 			} | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = URLPlugin; | ||||
|  | @ -2551,6 +2551,10 @@ | |||
|             } | ||||
|           ] | ||||
|         }, | ||||
|         "dependency": { | ||||
|           "description": "Match dependency type.", | ||||
|           "type": "string" | ||||
|         }, | ||||
|         "descriptionData": { | ||||
|           "description": "Match values of properties in the description file (usually package.json).", | ||||
|           "type": "object", | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| const currentDir = require("url").pathToFileURL(__dirname); | ||||
| 
 | ||||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe(currentDir + "/public/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,21 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "node", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]", | ||||
| 		publicPath: "public/" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| const currentDir = require("url").pathToFileURL(__dirname); | ||||
| 
 | ||||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe(currentDir + "/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,20 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "node", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,5 @@ | |||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe("file:///index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,21 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "node", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]", | ||||
| 		publicPath: "/" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,5 @@ | |||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe("https://test.cases/path/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,9 @@ | |||
| let _URL = require("url").URL; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	moduleScope(scope) { | ||||
| 		scope.URL = function URL(a, b) { | ||||
| 			return new _URL(a, b); | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1,20 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "web", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,5 @@ | |||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe("https://test.cases/path2/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,9 @@ | |||
| let _URL = require("url").URL; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	moduleScope(scope) { | ||||
| 		scope.URL = function URL(a, b) { | ||||
| 			return new _URL(a, b); | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1,21 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "web", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]", | ||||
| 		publicPath: "/path2/" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,5 @@ | |||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe("https://test.cases/path/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,9 @@ | |||
| let _URL = require("url").URL; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	moduleScope(scope) { | ||||
| 		scope.URL = function URL(a, b) { | ||||
| 			return new _URL(a, b); | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1,20 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "webworker", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1 @@ | |||
| a {} | ||||
|  | @ -0,0 +1,5 @@ | |||
| it("should handle import.meta.url in URL()", () => { | ||||
| 	const {href} = new URL("./index.css", import.meta.url); | ||||
| 
 | ||||
| 	expect(href).toBe("https://test.cases/index.css"); | ||||
| }); | ||||
|  | @ -0,0 +1,9 @@ | |||
| let _URL = require("url").URL; | ||||
| 
 | ||||
| module.exports = { | ||||
| 	moduleScope(scope) { | ||||
| 		scope.URL = function URL(a, b) { | ||||
| 			return new _URL(a, b); | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
|  | @ -0,0 +1,21 @@ | |||
| /** @type {import("../../../../").Configuration} */ | ||||
| module.exports = { | ||||
| 	mode: "development", | ||||
| 	target: "webworker", | ||||
| 	devtool: false, | ||||
| 	output: { | ||||
| 		assetModuleFilename: "[name][ext]", | ||||
| 		publicPath: "/" | ||||
| 	}, | ||||
| 	module: { | ||||
| 		rules: [ | ||||
| 			{ | ||||
| 				test: /\.css$/, | ||||
| 				type: "asset/resource" | ||||
| 			} | ||||
| 		] | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		asset: true | ||||
| 	} | ||||
| }; | ||||
|  | @ -1,6 +1,7 @@ | |||
| module.exports = class FakeDocument { | ||||
| 	constructor() { | ||||
| 		this.head = this.createElement("head"); | ||||
| 		this.baseURI = "https://test.cases/path/index.html"; | ||||
| 		this._elementsByTagName = new Map([["head", [this.head]]]); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -6948,6 +6948,11 @@ declare interface RuleSetRule { | |||
| 	 */ | ||||
| 	compiler?: RuleSetCondition; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Match dependency type. | ||||
| 	 */ | ||||
| 	dependency?: string; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Match values of properties in the description file (usually package.json). | ||||
| 	 */ | ||||
|  | @ -8990,6 +8995,7 @@ declare namespace exports { | |||
| 		export let system: string; | ||||
| 		export let hasOwnProperty: string; | ||||
| 		export let systemContext: string; | ||||
| 		export let baseURI: string; | ||||
| 	} | ||||
| 	export const UsageState: Readonly<{ | ||||
| 		Unused: 0; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue