Skip to content

Commit

Permalink
Support asset imports (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperisager authored Oct 2, 2024
1 parent 34390d9 commit d9d3a33
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 2 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ Options include:
}
```
#### `const url = Module.asset(specifier, parentURL[, options])`
Options include:
```js
{
referrer = null,
protocol,
imports,
resolutions,
conditions
}
```
#### `module.url`
#### `module.filename`
Expand Down
50 changes: 50 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ const Module = module.exports = exports = class Module {
meta.cache = module._cache
meta.resolve = resolve
meta.addon = addon
meta.asset = asset

function resolve (specifier) {
const resolved = self.resolve(specifier, referrer._url, { referrer })
Expand All @@ -341,6 +342,10 @@ const Module = module.exports = exports = class Module {

return addon._exports
}

function asset (specifier) {
return self.asset(specifier, referrer._url, { referrer }).href
}
}

static _onrun (reason, promise, err = reason) {
Expand Down Expand Up @@ -489,6 +494,46 @@ const Module = module.exports = exports = class Module {
}
}

static asset (specifier, parentURL, opts = {}) {
const self = Module

if (typeof specifier !== 'string') {
throw new TypeError(`Specifier must be a string. Received type ${typeof specifier} (${specifier})`)
}

const {
referrer = null,
protocol = referrer ? referrer._protocol : self._protocol,
imports = referrer ? referrer._imports : null,
resolutions = referrer ? referrer._resolutions : null,
conditions = referrer ? referrer._conditions : self._conditions
} = opts

const [resolution = null] = resolve(specifier, parentURL, {
conditions,
imports,
resolutions
}, readPackage)

if (resolution !== null && protocol.exists(resolution)) {
return protocol.asset(resolution)
}

let msg = `Cannot find asset '${specifier}'`

if (referrer) msg += ` imported from '${referrer._url.href}'`

throw errors.ASSET_NOT_FOUND(msg)

function readPackage (packageURL) {
if (protocol.exists(packageURL)) {
return Module.load(packageURL, { protocol })._exports
}

return null
}
}

static _extensionFor (type) {
switch (type) {
case constants.types.SCRIPT:
Expand Down Expand Up @@ -568,6 +613,7 @@ const createRequire = exports.createRequire = function createRequire (parentURL,
require.cache = module._cache
require.resolve = resolve
require.addon = addon
require.asset = asset

return require

Expand Down Expand Up @@ -595,6 +641,10 @@ const createRequire = exports.createRequire = function createRequire (parentURL,

return addon._exports
}

function asset (specifier) {
return urlToPath(self.asset(specifier, referrer._url, { referrer }))
}
}

if (Bare.simulator) Module._conditions.push('simulator')
Expand Down
4 changes: 4 additions & 0 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ module.exports = class ModuleError extends Error {
return new ModuleError(msg, 'MODULE_NOT_FOUND', ModuleError.MODULE_NOT_FOUND)
}

static ASSET_NOT_FOUND (msg) {
return new ModuleError(msg, 'ASSET_NOT_FOUND', ModuleError.ASSET_NOT_FOUND)
}

static UNKNOWN_PROTOCOL (msg) {
return new ModuleError(msg, 'UNKNOWN_PROTOCOL', ModuleError.UNKNOWN_PROTOCOL)
}
Expand Down
7 changes: 6 additions & 1 deletion lib/protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module.exports = class ModuleProtocol {
'resolve',
'exists',
'read',
'load'
'load',
'asset'
]) {
const method = methods[name]

Expand Down Expand Up @@ -40,6 +41,10 @@ module.exports = class ModuleProtocol {
return null
}

asset (url) {
return url
}

extend (methods) {
return new ModuleProtocol(methods, this)
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"homepage": "https://github.com/holepunchto/bare-module#readme",
"dependencies": {
"bare-bundle": "^1.0.0",
"bare-bundle": "^1.3.0",
"bare-module-resolve": "^1.6.0",
"bare-path": "^3.0.0",
"bare-url": "^2.0.1",
Expand Down
120 changes: 120 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2585,6 +2585,126 @@ test('extend the default protocol', (t) => {
t.is(Module.load(pathToFileURL('test/fixtures/foo.js'), { protocol }).exports, 'modified')
})

test('load .js with asset import', (t) => {
t.teardown(onteardown)

const protocol = new Module.Protocol({
exists (url) {
return url.href === root + '/foo.txt'
},

read (url) {
if (url.href === root + '/index.js') {
return 'module.exports = require.asset(\'./foo.txt\')'
}

t.fail()
}
})

t.is(Module.load(new URL(root + '/index.js'), { protocol }).exports, isWindows ? 'c:\\foo.txt' : '/foo.txt')
})

test('load .cjs with asset import', (t) => {
t.teardown(onteardown)

const protocol = new Module.Protocol({
exists (url) {
return url.href === root + '/foo.txt'
},

read (url) {
if (url.href === root + '/index.cjs') {
return 'module.exports = require.asset(\'./foo.txt\')'
}

t.fail()
}
})

t.is(Module.load(new URL(root + '/index.cjs'), { protocol }).exports, isWindows ? 'c:\\foo.txt' : '/foo.txt')
})

test('load .mjs with asset import', (t) => {
t.teardown(onteardown)

const protocol = new Module.Protocol({
exists (url) {
return url.href === root + '/foo.txt'
},

read (url) {
if (url.href === root + '/index.mjs') {
return 'export default import.meta.asset(\'./foo.txt\')'
}

t.fail()
}
})

t.is(Module.load(new URL(root + '/index.mjs'), { protocol }).exports.default, root + '/foo.txt')
})

test('load .js with asset import, asset method', (t) => {
t.teardown(onteardown)

const protocol = new Module.Protocol({
exists (url) {
return url.href === root + '/foo.txt'
},

read (url) {
if (url.href === root + '/index.js') {
return 'module.exports = require.asset(\'./foo.txt\')'
}

t.fail()
},

asset (url) {
if (url.href === root + '/foo.txt') {
return new URL(root + '/bar.txt')
}

return url
}
})

t.is(Module.load(new URL(root + '/index.js'), { protocol }).exports, isWindows ? 'c:\\bar.txt' : '/bar.txt')
})

test('load .bundle with asset import', (t) => {
t.teardown(onteardown)

const bundle = new Module.Bundle()
.write('/foo.js', 'module.exports = require.asset(\'./bar.txt\')', { main: true })
.write('/bar.txt', 'hello world', { asset: true })
.toBuffer()

t.is(Module.load(new URL(root + '/app.bundle'), bundle).exports, isWindows ? 'c:\\app.bundle\\bar.txt' : '/app.bundle/bar.txt')
})

test('load .bundle with asset import, asset method', (t) => {
t.teardown(onteardown)

const bundle = new Module.Bundle()
.write('/foo.js', 'module.exports = require.asset(\'./bar.txt\')', { main: true })
.write('/bar.txt', 'hello world', { asset: true })
.toBuffer()

const protocol = new Module.Protocol({
asset (url) {
if (url.href === root + '/app.bundle/bar.txt') {
return new URL(root + '/bar.txt')
}

return url
}
})

t.is(Module.load(new URL(root + '/app.bundle'), bundle, { protocol }).exports, isWindows ? 'c:\\bar.txt' : '/bar.txt')
})

function onteardown () {
// TODO Provide a public API for clearing the cache.
Module._cache = Object.create(null)
Expand Down

0 comments on commit d9d3a33

Please sign in to comment.