nuxt-content-assets 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +276 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.ts +12 -0
- package/dist/module.json +5 -0
- package/dist/module.mjs +180 -0
- package/dist/runtime/server/plugins/plugin.d.ts +2 -0
- package/dist/runtime/server/plugins/plugin.mjs +52 -0
- package/dist/types.d.ts +10 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Nuxt Content Assets
|
|
2
|
+
|
|
3
|
+
> Enable locally-located assets in Nuxt Content
|
|
4
|
+
|
|
5
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
6
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
7
|
+
[![License][license-src]][license-href]
|
|
8
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Nuxt Content Assets enables locally-located assets in your [Nuxt Content](https://content.nuxtjs.org/) folder:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
+- content
|
|
16
|
+
+- posts
|
|
17
|
+
+- 2023-01-01
|
|
18
|
+
+- index.md
|
|
19
|
+
+- media
|
|
20
|
+
+- featured.png
|
|
21
|
+
+- mountains.jpg
|
|
22
|
+
+- seaside.mp4
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
In your documents, simply reference assets using relative paths:
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
---
|
|
29
|
+
title: Summer Holiday
|
|
30
|
+
featured: media/featured.png
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
I loved being in the mountains.
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
Almost as much as being in the sea!
|
|
38
|
+
|
|
39
|
+
<video src="media/seaside.mp4"></video>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The module:
|
|
43
|
+
|
|
44
|
+
- supports configurable image, media and file types
|
|
45
|
+
- supports appropriate HTML tags
|
|
46
|
+
- converts markdown and frontmatter content
|
|
47
|
+
|
|
48
|
+
## Demo
|
|
49
|
+
|
|
50
|
+
To run the demo online, go to:
|
|
51
|
+
|
|
52
|
+
- https://stackblitz.com/github/davestewart/nuxt-content-assets?file=demo%2Fapp.vue
|
|
53
|
+
|
|
54
|
+
You can browse the demo files in:
|
|
55
|
+
|
|
56
|
+
- .https://github.com/davestewart/nuxt-content-assets/tree/main/demo
|
|
57
|
+
|
|
58
|
+
To run the demo locally, clone the application and from the root, run:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
npm run demo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Setup
|
|
65
|
+
|
|
66
|
+
Install the dependency:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install nuxt-content-assets
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Configure `nuxt.config.ts`:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
export default defineNuxtConfig({
|
|
76
|
+
modules: [
|
|
77
|
+
'nuxt-content-relative-assets', // make sure to add before content!
|
|
78
|
+
'@nuxt/content',
|
|
79
|
+
]
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run the dev server or build and assets should now be served alongside markdown content.
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
### Overview
|
|
88
|
+
|
|
89
|
+
Once the build or dev server is running, paths should be rewritten and assets served automatically.
|
|
90
|
+
|
|
91
|
+
Here's how it works:
|
|
92
|
+
|
|
93
|
+
- the module scans content folders for assets
|
|
94
|
+
- these are copied to a temporary build folder
|
|
95
|
+
- matching relative paths in markdown are updated
|
|
96
|
+
- Nitro serves the assets from the new location
|
|
97
|
+
|
|
98
|
+
Right now, if you change any assets, you will need to re-run the dev server / build.
|
|
99
|
+
|
|
100
|
+
### Supported formats
|
|
101
|
+
|
|
102
|
+
The following file formats are supported out of the box:
|
|
103
|
+
|
|
104
|
+
| Type | Extensions |
|
|
105
|
+
|--------|-------------------------------------------------------------------------|
|
|
106
|
+
| Images | `png`, `jpg`, `jpeg`, `gif`, `svg`, `webp` |
|
|
107
|
+
| Media | `mp3`, `m4a`, `wav`, `mp4`, `mov`, `webm`, `ogg`, `avi`, `flv`, `avchd` |
|
|
108
|
+
| Files | `pdf`, `doc`, `docx`, `xls`, `xlsx`, `ppt`, `pptx`, `odp`, `key` |
|
|
109
|
+
|
|
110
|
+
See the [configuration](#output) section for more options.
|
|
111
|
+
|
|
112
|
+
### Images
|
|
113
|
+
|
|
114
|
+
The module [optionally](#image-attributes) writes `width` and `height` attributes to the generated HTML:
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<img src="..." width="640" height="480">
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This locks the aspect ratio of the image preventing content jumps.
|
|
121
|
+
|
|
122
|
+
If you use custom [ProseImg](https://content.nuxtjs.org/api/components/prose) components, you can even grab these values using the Vue `$attrs` property:
|
|
123
|
+
|
|
124
|
+
```vue
|
|
125
|
+
<template>
|
|
126
|
+
<div class="image">
|
|
127
|
+
<img :src="$attrs.src" :style="`aspect-ratio:${$attrs.width}/${$attrs.height}`" />
|
|
128
|
+
</div>
|
|
129
|
+
</template>
|
|
130
|
+
|
|
131
|
+
<script>
|
|
132
|
+
export default {
|
|
133
|
+
inheritAttrs: false
|
|
134
|
+
}
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See the [Demo](demo/components/_content/ProseImg.vue) for an example.
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
The module **doesn't require** any configuration, but you can tweak the following settings:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
// nuxt.config.ts
|
|
146
|
+
export default defineNuxtConfig({
|
|
147
|
+
'content-assets': {
|
|
148
|
+
// where to generate and serve the assets from
|
|
149
|
+
output: 'assets/content/[path]/[file]',
|
|
150
|
+
|
|
151
|
+
// add additional extensions
|
|
152
|
+
additionalExtensions: 'html',
|
|
153
|
+
|
|
154
|
+
// completely replace supported extensions
|
|
155
|
+
extensions: 'png jpg',
|
|
156
|
+
|
|
157
|
+
// add image width and height
|
|
158
|
+
imageAttrs: true,
|
|
159
|
+
|
|
160
|
+
// print debug messages to the console
|
|
161
|
+
debug: true,
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Output
|
|
167
|
+
|
|
168
|
+
The output path can be customised using a template string:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
assets/img/content/[path]/[name][extname]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The first part of the path should be public root-relative folder:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
assets/img/content
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The optional second part of the path indicates specific placement of each image:
|
|
181
|
+
|
|
182
|
+
| Token | Description | Example |
|
|
183
|
+
|-------------|--------------------------------------------|--------------------|
|
|
184
|
+
| `[folder]` | The relative folder of the file | `posts/2023-01-01` |
|
|
185
|
+
| `[file]` | The full filename of the file | `featured.jpg` |
|
|
186
|
+
| `[name]` | The name of the file without the extension | `featured` |
|
|
187
|
+
| `[hash]` | A hash of the absolute source path | `9M00N4l9A0` |
|
|
188
|
+
| `[extname]` | The full extension with the dot | `.jpg` |
|
|
189
|
+
| `[ext]` | The extension without the dot | `jpg` |
|
|
190
|
+
|
|
191
|
+
For example:
|
|
192
|
+
|
|
193
|
+
| Template | Output |
|
|
194
|
+
|--------------------------------------|----------------------------------------------------|
|
|
195
|
+
| `assets/img/content/[folder]/[file]` | `assets/img/content/posts/2023-01-01/featured.jpg` |
|
|
196
|
+
| `assets/img/[name]-[hash].[ext]` | `assets/img/featured-9M00N4l9A0.jpg` |
|
|
197
|
+
| `content/[hash].[ext]` | `content/9M00N4l9A0.jpg` |
|
|
198
|
+
|
|
199
|
+
Note that the module defaults to all files in a single folder:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
/assets/content/[name]-[hash].[ext]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Extensions
|
|
206
|
+
|
|
207
|
+
You can add or replace supported extensions if you need to:
|
|
208
|
+
|
|
209
|
+
To add extensions, use `additionalExtensions`:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
{
|
|
213
|
+
additionalExtensions: 'html' // add support for html
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
To completely replace supported extensions, use `extensions`:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
{
|
|
221
|
+
extensions: 'png jpg' // serve png and jpg files only
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Image attributes
|
|
226
|
+
|
|
227
|
+
The module automatically adds `width` and `height` attributes to images.
|
|
228
|
+
|
|
229
|
+
Opt out of this by passing `false`:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
{
|
|
233
|
+
imageAttrs: false
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
Should you wish to develop the project, the scripts are:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# install dependencies
|
|
243
|
+
npm install
|
|
244
|
+
|
|
245
|
+
# generate type stubs
|
|
246
|
+
npm run dev:prepare
|
|
247
|
+
|
|
248
|
+
# develop with the demo
|
|
249
|
+
npm run dev
|
|
250
|
+
|
|
251
|
+
# build the demo
|
|
252
|
+
npm run dev:build
|
|
253
|
+
|
|
254
|
+
# run eslint
|
|
255
|
+
npm run lint
|
|
256
|
+
|
|
257
|
+
# run vitest
|
|
258
|
+
npm run test
|
|
259
|
+
npm run test:watch
|
|
260
|
+
|
|
261
|
+
# release new version
|
|
262
|
+
npm run release
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
<!-- Badges -->
|
|
266
|
+
[npm-version-src]: https://img.shields.io/npm/v/nuxt-content-assets/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
267
|
+
[npm-version-href]: https://npmjs.com/package/nuxt-content-assets
|
|
268
|
+
|
|
269
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-content-assets.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
270
|
+
[npm-downloads-href]: https://npmjs.com/package/nuxt-content-assets
|
|
271
|
+
|
|
272
|
+
[license-src]: https://img.shields.io/npm/l/nuxt-content-assets.svg?style=flat&colorA=18181B&colorB=28CF8D
|
|
273
|
+
[license-href]: https://npmjs.com/package/nuxt-content-assets
|
|
274
|
+
|
|
275
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
|
|
276
|
+
[nuxt-href]: https://nuxt.com
|
package/dist/module.cjs
ADDED
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface ModuleOptions {
|
|
4
|
+
output?: string;
|
|
5
|
+
additionalExtensions?: string;
|
|
6
|
+
extensions?: string;
|
|
7
|
+
imageAttrs?: boolean;
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
11
|
+
|
|
12
|
+
export { ModuleOptions, _default as default };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createResolver, defineNuxtModule, addTemplate } from '@nuxt/kit';
|
|
2
|
+
import getImageSize from 'image-size';
|
|
3
|
+
import glob from 'glob';
|
|
4
|
+
import * as Fs from 'fs';
|
|
5
|
+
import * as Path from 'path';
|
|
6
|
+
import Path__default from 'path';
|
|
7
|
+
import { hash } from 'ohash';
|
|
8
|
+
|
|
9
|
+
function log(...data) {
|
|
10
|
+
console.info(`[${name}]`, ...data);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function matchWords(value) {
|
|
14
|
+
return value.match(/\w+/g) || [];
|
|
15
|
+
}
|
|
16
|
+
function isImage(path) {
|
|
17
|
+
const ext = Path__default.extname(path).substring(1);
|
|
18
|
+
return imageExtensions.includes(ext);
|
|
19
|
+
}
|
|
20
|
+
function interpolatePattern(pattern, src, dir, warn = false) {
|
|
21
|
+
return Path__default.join(pattern.replace(/\[\w+]/g, (match) => {
|
|
22
|
+
const name = match.substring(1, match.length - 1);
|
|
23
|
+
const fn = replacers[name];
|
|
24
|
+
if (fn) {
|
|
25
|
+
return fn(src, dir);
|
|
26
|
+
}
|
|
27
|
+
if (warn) {
|
|
28
|
+
log(`Unknown output token ${match}`, true);
|
|
29
|
+
}
|
|
30
|
+
return match;
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
const replacers = {
|
|
34
|
+
folder: (src, dir) => Path__default.dirname(src.replace(dir, "")),
|
|
35
|
+
file: (src) => Path__default.basename(src),
|
|
36
|
+
name: (src) => Path__default.basename(src, Path__default.extname(src)),
|
|
37
|
+
extname: (src) => Path__default.extname(src),
|
|
38
|
+
ext: (src) => Path__default.extname(src).substring(1),
|
|
39
|
+
hash: (src) => hash({ src })
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const name = "content-assets";
|
|
43
|
+
const defaults = {
|
|
44
|
+
assetsDir: "assets/content",
|
|
45
|
+
assetsPattern: "[name]-[hash].[ext]"
|
|
46
|
+
};
|
|
47
|
+
const imageExtensions = matchWords("png jpg jpeg gif svg webp ico");
|
|
48
|
+
const mediaExtensions = matchWords("mp3 m4a wav mp4 mov webm ogg avi flv avchd");
|
|
49
|
+
const fileExtensions = matchWords("pdf doc docx xls xlsx ppt pptx odp key");
|
|
50
|
+
const extensions = [
|
|
51
|
+
...imageExtensions,
|
|
52
|
+
...mediaExtensions,
|
|
53
|
+
...fileExtensions
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
function getSources(sources) {
|
|
57
|
+
return Object.keys(sources).reduce((output, key) => {
|
|
58
|
+
const source = sources[key];
|
|
59
|
+
if (source) {
|
|
60
|
+
const { driver, base } = source;
|
|
61
|
+
if (driver === "fs") {
|
|
62
|
+
output[key] = base;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return output;
|
|
66
|
+
}, {});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const resolve = createResolver(import.meta.url).resolve;
|
|
70
|
+
const module = defineNuxtModule({
|
|
71
|
+
meta: {
|
|
72
|
+
name
|
|
73
|
+
},
|
|
74
|
+
defaults: {
|
|
75
|
+
output: `${defaults.assetsDir}/${defaults.assetsPattern}`,
|
|
76
|
+
extensions: "",
|
|
77
|
+
additionalExtensions: "",
|
|
78
|
+
imageAttrs: true,
|
|
79
|
+
debug: false
|
|
80
|
+
},
|
|
81
|
+
setup(options, nuxt) {
|
|
82
|
+
var _a;
|
|
83
|
+
const pluginPath = resolve("./runtime/server") + "/plugins/plugin";
|
|
84
|
+
const buildPath = nuxt.options.buildDir;
|
|
85
|
+
const cachePath = Path.resolve(buildPath, "content-assets");
|
|
86
|
+
if (options.debug) {
|
|
87
|
+
log("Removing cache folders...");
|
|
88
|
+
}
|
|
89
|
+
Fs.rmSync(Path.join(buildPath, "content-cache"), { recursive: true, force: true });
|
|
90
|
+
Fs.rmSync(cachePath, { recursive: true, force: true });
|
|
91
|
+
const sources = nuxt.options._layers.map((layer) => layer.config?.content?.sources).reduce((output2, sources2) => {
|
|
92
|
+
if (sources2) {
|
|
93
|
+
Object.assign(output2, getSources(sources2));
|
|
94
|
+
}
|
|
95
|
+
return output2;
|
|
96
|
+
}, {});
|
|
97
|
+
if (Object.keys(sources).length === 0 || !sources.content) {
|
|
98
|
+
const content = nuxt.options.srcDir + "/content";
|
|
99
|
+
if (Fs.existsSync(content)) {
|
|
100
|
+
sources.content = content;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const output = options.output || defaults.assetsDir;
|
|
104
|
+
const matches = output.match(/([^[]+)(.*)?/);
|
|
105
|
+
const assetsDir = matches ? matches[1] : defaults.assetsDir;
|
|
106
|
+
const assetsPattern = (matches ? matches[2] : "") || defaults.assetsPattern;
|
|
107
|
+
interpolatePattern(assetsPattern, "", "", true);
|
|
108
|
+
if (options.extensions?.trim()) {
|
|
109
|
+
extensions.splice(0, extensions.length, ...matchWords(options.extensions));
|
|
110
|
+
} else if (options.additionalExtensions) {
|
|
111
|
+
extensions.push(...matchWords(options.additionalExtensions));
|
|
112
|
+
}
|
|
113
|
+
if (nuxt.options.content) {
|
|
114
|
+
(_a = nuxt.options.content).ignores || (_a.ignores = []);
|
|
115
|
+
}
|
|
116
|
+
function getAssetConfig(pattern, src, dir) {
|
|
117
|
+
let width = void 0;
|
|
118
|
+
let height = void 0;
|
|
119
|
+
if (options.imageAttrs && isImage(src)) {
|
|
120
|
+
const size = getImageSize(src);
|
|
121
|
+
width = size.width;
|
|
122
|
+
height = size.height;
|
|
123
|
+
}
|
|
124
|
+
const id = Path.join(Path.basename(dir), Path.relative(dir, src)).replaceAll("/", ":");
|
|
125
|
+
const file = interpolatePattern(pattern, src, dir);
|
|
126
|
+
const trg = Path.join(cachePath, assetsDir, file);
|
|
127
|
+
const rel = Path.join("/", assetsDir, file);
|
|
128
|
+
return { id, file, trg, rel, width, height };
|
|
129
|
+
}
|
|
130
|
+
const publicFolder = Path.join(cachePath, assetsDir);
|
|
131
|
+
const sourceFolders = Object.values(sources);
|
|
132
|
+
const assets = {};
|
|
133
|
+
sourceFolders.forEach((folder) => {
|
|
134
|
+
const pattern = `${folder}/**/*.{${extensions.join(",")}}`;
|
|
135
|
+
const paths = glob.globSync(pattern);
|
|
136
|
+
paths.forEach((src) => {
|
|
137
|
+
const config = getAssetConfig(assetsPattern, src, folder);
|
|
138
|
+
nuxt.options.content.ignores.push(config.id);
|
|
139
|
+
assets[src] = config;
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
nuxt.hook("build:before", function() {
|
|
143
|
+
Fs.mkdirSync(publicFolder, { recursive: true });
|
|
144
|
+
if (options.debug) {
|
|
145
|
+
const paths = Object.keys(assets).map((key) => " - " + assets[key].id.replaceAll(":", "/"));
|
|
146
|
+
log(`Copying ${Object.keys(assets).length} assets:
|
|
147
|
+
|
|
148
|
+
${paths.join("\n")}
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
151
|
+
Object.keys(assets).forEach((src) => {
|
|
152
|
+
const { trg } = assets[src];
|
|
153
|
+
const trgFolder = Path.dirname(trg);
|
|
154
|
+
Fs.mkdirSync(trgFolder, { recursive: true });
|
|
155
|
+
Fs.copyFileSync(src, trg);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
const virtualConfig = [
|
|
159
|
+
`export const assets = ${JSON.stringify(assets)}`,
|
|
160
|
+
`export const sources = ${JSON.stringify(sources)}`
|
|
161
|
+
].join("\n");
|
|
162
|
+
nuxt.options.alias[`#${name}`] = addTemplate({
|
|
163
|
+
filename: `${name}.mjs`,
|
|
164
|
+
getContents: () => virtualConfig
|
|
165
|
+
}).dst;
|
|
166
|
+
nuxt.hook("nitro:config", async (config) => {
|
|
167
|
+
config.plugins || (config.plugins = []);
|
|
168
|
+
config.plugins.push(pluginPath);
|
|
169
|
+
config.virtual || (config.virtual = {});
|
|
170
|
+
config.virtual[`#${name}`] = virtualConfig;
|
|
171
|
+
config.publicAssets || (config.publicAssets = []);
|
|
172
|
+
config.publicAssets.push({
|
|
173
|
+
dir: cachePath
|
|
174
|
+
// maxAge: 60 * 60 * 24 * 365 // 1 year
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
export { module as default };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Path from "path";
|
|
2
|
+
import { visit } from "unist-util-visit";
|
|
3
|
+
import { assets, sources } from "#content-assets";
|
|
4
|
+
import { tags } from "../../../config";
|
|
5
|
+
import { isValidAsset, walk } from "../../../utils";
|
|
6
|
+
function getDocPath(id) {
|
|
7
|
+
const parts = id.split(":");
|
|
8
|
+
const key = parts.shift();
|
|
9
|
+
const relPath = parts.join("/");
|
|
10
|
+
const absBase = sources[key];
|
|
11
|
+
return Path.join(absBase, relPath);
|
|
12
|
+
}
|
|
13
|
+
function getAsset(absDoc, relAsset) {
|
|
14
|
+
const absAsset = Path.join(Path.dirname(absDoc), relAsset);
|
|
15
|
+
return assets[absAsset] || {};
|
|
16
|
+
}
|
|
17
|
+
export default defineNitroPlugin(async (nitroApp) => {
|
|
18
|
+
nitroApp.hooks.hook("content:file:afterParse", async (file) => {
|
|
19
|
+
if (file._id.endsWith(".md")) {
|
|
20
|
+
const absDoc = getDocPath(file._id);
|
|
21
|
+
const filter = (value, key) => !(String(key).startsWith("_") || key === "body");
|
|
22
|
+
walk(file, (value, parent, key) => {
|
|
23
|
+
if (isValidAsset(value)) {
|
|
24
|
+
const { rel } = getAsset(absDoc, value);
|
|
25
|
+
if (rel) {
|
|
26
|
+
parent[key] = rel;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}, filter);
|
|
30
|
+
visit(file.body, (n) => tags.includes(n.tag), (node) => {
|
|
31
|
+
if (node.props.src) {
|
|
32
|
+
const { rel, width, height } = getAsset(absDoc, node.props.src);
|
|
33
|
+
if (rel) {
|
|
34
|
+
node.props.src = rel;
|
|
35
|
+
}
|
|
36
|
+
if (width && height) {
|
|
37
|
+
node.props.width = width;
|
|
38
|
+
node.props.height = height;
|
|
39
|
+
}
|
|
40
|
+
} else if (node.tag === "a") {
|
|
41
|
+
if (node.props.href) {
|
|
42
|
+
const { rel } = getAsset(absDoc, node.props.href);
|
|
43
|
+
if (rel) {
|
|
44
|
+
node.props.href = rel;
|
|
45
|
+
node.props.target = "_blank";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
import { ModuleOptions } from './module'
|
|
3
|
+
|
|
4
|
+
declare module '@nuxt/schema' {
|
|
5
|
+
interface NuxtConfig { ['content-assets']?: Partial<ModuleOptions> }
|
|
6
|
+
interface NuxtOptions { ['content-assets']?: ModuleOptions }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export { ModuleOptions, default } from './module'
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuxt-content-assets",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Enable locally-located assets in Nuxt Content",
|
|
5
|
+
"repository": "davestewart/nuxt-content-assets",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/types.d.ts",
|
|
11
|
+
"import": "./dist/module.mjs",
|
|
12
|
+
"require": "./dist/module.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/module.cjs",
|
|
16
|
+
"types": "./dist/types.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"prepack": "nuxt-module-build",
|
|
22
|
+
"dev": "nuxi dev demo",
|
|
23
|
+
"dev:build": "nuxi build demo",
|
|
24
|
+
"dev:prepare": "nuxt-module-build --stub && nuxi prepare demo",
|
|
25
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
26
|
+
"lint": "eslint .",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest watch"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@nuxt/kit": "^3.3.2",
|
|
32
|
+
"glob": "^9.3.2",
|
|
33
|
+
"image-size": "^1.0.2",
|
|
34
|
+
"ohash": "^1.0.0",
|
|
35
|
+
"unist-util-visit": "^4.1.2"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@nuxt/content": "latest"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@nuxt/content": "latest",
|
|
42
|
+
"@nuxt/eslint-config": "^0.1.1",
|
|
43
|
+
"@nuxt/module-builder": "^0.2.1",
|
|
44
|
+
"@nuxt/schema": "^3.3.2",
|
|
45
|
+
"@nuxt/test-utils": "^3.3.2",
|
|
46
|
+
"changelogen": "^0.5.1",
|
|
47
|
+
"eslint": "^8.36.0",
|
|
48
|
+
"nuxt": "^3.3.2",
|
|
49
|
+
"vitest": "^0.29.7"
|
|
50
|
+
}
|
|
51
|
+
}
|