astro 2.1.4 → 2.1.5

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.
@@ -5,6 +5,7 @@ import type { MarkdownHeading, MarkdownMetadata, MarkdownRenderingResult, Rehype
5
5
  import type * as babel from '@babel/core';
6
6
  import type { OutgoingHttpHeaders } from 'http';
7
7
  import type { AddressInfo } from 'net';
8
+ import type * as rollup from 'rollup';
8
9
  import type { TsConfigJson } from 'tsconfig-resolver';
9
10
  import type * as vite from 'vite';
10
11
  import type { z } from 'zod';
@@ -956,12 +957,26 @@ export interface InjectedRoute {
956
957
  export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
957
958
  integrations: AstroIntegration[];
958
959
  }
960
+ export type ContentEntryModule = {
961
+ id: string;
962
+ collection: string;
963
+ slug: string;
964
+ body: string;
965
+ data: Record<string, unknown>;
966
+ _internal: {
967
+ rawData: string;
968
+ filePath: string;
969
+ };
970
+ };
959
971
  export interface ContentEntryType {
960
972
  extensions: string[];
961
973
  getEntryInfo(params: {
962
974
  fileUrl: URL;
963
975
  contents: string;
964
976
  }): GetEntryInfoReturnType | Promise<GetEntryInfoReturnType>;
977
+ getRenderModule?(params: {
978
+ entry: ContentEntryModule;
979
+ }): rollup.LoadResult | Promise<rollup.LoadResult>;
965
980
  contentModuleTypes?: string;
966
981
  }
967
982
  type GetEntryInfoReturnType = {
@@ -5,4 +5,4 @@ import type { AstroSettings } from '../@types/astro.js';
5
5
  export declare function astroContentImportPlugin({ fs, settings, }: {
6
6
  fs: typeof fsMod;
7
7
  settings: AstroSettings;
8
- }): Plugin;
8
+ }): Plugin[];
@@ -13,12 +13,25 @@ import {
13
13
  getEntrySlug,
14
14
  getEntryType,
15
15
  globalContentConfigObserver,
16
+ NoCollectionError,
16
17
  patchAssets
17
18
  } from "./utils.js";
18
19
  function isContentFlagImport(viteId, contentEntryExts) {
19
20
  const { searchParams, pathname } = new URL(viteId, "file://");
20
21
  return searchParams.has(CONTENT_FLAG) && contentEntryExts.some((ext) => pathname.endsWith(ext));
21
22
  }
23
+ function getContentRendererByViteId(viteId, settings) {
24
+ let ext = viteId.split(".").pop();
25
+ if (!ext)
26
+ return void 0;
27
+ for (const contentEntryType of settings.contentEntryTypes) {
28
+ if (Boolean(contentEntryType.getRenderModule) && contentEntryType.extensions.includes("." + ext)) {
29
+ return contentEntryType.getRenderModule;
30
+ }
31
+ }
32
+ return void 0;
33
+ }
34
+ const CHOKIDAR_MODIFIED_EVENTS = ["add", "unlink", "change"];
22
35
  function astroContentImportPlugin({
23
36
  fs,
24
37
  settings
@@ -31,98 +44,171 @@ function astroContentImportPlugin({
31
44
  contentEntryExtToParser.set(ext, entryType);
32
45
  }
33
46
  }
34
- return {
35
- name: "astro:content-imports",
36
- async load(id) {
37
- const { fileId } = getFileInfo(id, settings.config);
38
- if (isContentFlagImport(id, contentEntryExts)) {
39
- const observable = globalContentConfigObserver.get();
40
- if (observable.status === "init") {
41
- throw new AstroError({
42
- ...AstroErrorData.UnknownContentCollectionError,
43
- message: "Content config failed to load."
44
- });
45
- }
46
- if (observable.status === "error") {
47
- throw observable.error;
48
- }
49
- let contentConfig = observable.status === "loaded" ? observable.config : void 0;
50
- if (observable.status === "loading") {
51
- contentConfig = await new Promise((resolve) => {
52
- const unsubscribe = globalContentConfigObserver.subscribe((ctx) => {
53
- if (ctx.status === "loaded") {
54
- resolve(ctx.config);
55
- unsubscribe();
56
- } else if (ctx.status === "error") {
57
- resolve(void 0);
58
- unsubscribe();
59
- }
60
- });
61
- });
62
- }
63
- const rawContents = await fs.promises.readFile(fileId, "utf-8");
64
- const fileExt = extname(fileId);
65
- if (!contentEntryExtToParser.has(fileExt)) {
66
- throw new AstroError({
67
- ...AstroErrorData.UnknownContentCollectionError,
68
- message: `No parser found for content entry ${JSON.stringify(
69
- fileId
70
- )}. Did you apply an integration for this file type?`
47
+ const plugins = [
48
+ {
49
+ name: "astro:content-imports",
50
+ async load(viteId) {
51
+ if (isContentFlagImport(viteId, contentEntryExts)) {
52
+ const { fileId } = getFileInfo(viteId, settings.config);
53
+ const { id, slug, collection, body, data, _internal } = await setContentEntryModuleCache({
54
+ fileId,
55
+ pluginContext: this
71
56
  });
72
- }
73
- const contentEntryParser = contentEntryExtToParser.get(fileExt);
74
- const info = await contentEntryParser.getEntryInfo({
75
- fileUrl: pathToFileURL(fileId),
76
- contents: rawContents
77
- });
78
- const generatedInfo = getEntryInfo({
79
- entry: pathToFileURL(fileId),
80
- contentDir: contentPaths.contentDir
81
- });
82
- if (generatedInfo instanceof Error)
83
- return;
84
- const _internal = { filePath: fileId, rawData: info.rawData };
85
- const slug = getEntrySlug({ ...generatedInfo, unvalidatedSlug: info.slug });
86
- const collectionConfig = contentConfig == null ? void 0 : contentConfig.collections[generatedInfo.collection];
87
- let data = collectionConfig ? await getEntryData(
88
- { ...generatedInfo, _internal, unvalidatedData: info.data },
89
- collectionConfig
90
- ) : info.data;
91
- await patchAssets(data, this.meta.watchMode, this.emitFile, settings);
92
- const code = escapeViteEnvReferences(`
93
- export const id = ${JSON.stringify(generatedInfo.id)};
94
- export const collection = ${JSON.stringify(generatedInfo.collection)};
57
+ const code = escapeViteEnvReferences(`
58
+ export const id = ${JSON.stringify(id)};
59
+ export const collection = ${JSON.stringify(collection)};
95
60
  export const slug = ${JSON.stringify(slug)};
96
- export const body = ${JSON.stringify(info.body)};
61
+ export const body = ${JSON.stringify(body)};
97
62
  export const data = ${devalue.uneval(data)};
98
63
  export const _internal = {
99
64
  filePath: ${JSON.stringify(_internal.filePath)},
100
65
  rawData: ${JSON.stringify(_internal.rawData)},
101
66
  };
102
67
  `);
103
- return { code };
104
- }
105
- },
106
- configureServer(viteServer) {
107
- viteServer.watcher.on("all", async (event, entry) => {
108
- if (["add", "unlink", "change"].includes(event) && getEntryType(entry, contentPaths, contentEntryExts) === "config") {
109
- for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) {
110
- if (isContentFlagImport(modUrl, contentEntryExts)) {
111
- const mod = await viteServer.moduleGraph.getModuleByUrl(modUrl);
112
- if (mod) {
113
- viteServer.moduleGraph.invalidateModule(mod);
68
+ return { code };
69
+ }
70
+ },
71
+ configureServer(viteServer) {
72
+ viteServer.watcher.on("all", async (event, entry) => {
73
+ if (CHOKIDAR_MODIFIED_EVENTS.includes(event) && getEntryType(entry, contentPaths, contentEntryExts) === "config") {
74
+ for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) {
75
+ if (isContentFlagImport(modUrl, contentEntryExts) || Boolean(getContentRendererByViteId(modUrl, settings))) {
76
+ const mod = await viteServer.moduleGraph.getModuleByUrl(modUrl);
77
+ if (mod) {
78
+ viteServer.moduleGraph.invalidateModule(mod);
79
+ }
114
80
  }
115
81
  }
116
82
  }
83
+ });
84
+ },
85
+ async transform(code, id) {
86
+ if (isContentFlagImport(id, contentEntryExts)) {
87
+ return { code: escapeViteEnvReferences(code) };
88
+ }
89
+ }
90
+ }
91
+ ];
92
+ if (settings.contentEntryTypes.some((t) => t.getRenderModule)) {
93
+ plugins.push({
94
+ name: "astro:content-render-imports",
95
+ async load(viteId) {
96
+ const contentRenderer = getContentRendererByViteId(viteId, settings);
97
+ if (!contentRenderer)
98
+ return;
99
+ const { fileId } = getFileInfo(viteId, settings.config);
100
+ const entry = await getContentEntryModuleFromCache(fileId);
101
+ if (!entry) {
102
+ throw new AstroError({
103
+ ...AstroErrorData.UnknownContentCollectionError,
104
+ message: `Unable to render ${JSON.stringify(
105
+ fileId
106
+ )}. Did you import this module directly without using a content collection query?`
107
+ });
117
108
  }
109
+ return contentRenderer({ entry });
110
+ }
111
+ });
112
+ }
113
+ const contentEntryModuleByIdCache = /* @__PURE__ */ new Map();
114
+ function isAwaitingQueue(cacheEntry) {
115
+ return typeof cacheEntry === "object" && cacheEntry != null && "awaitingQueue" in cacheEntry;
116
+ }
117
+ function getContentEntryModuleFromCache(id) {
118
+ const cacheEntry = contentEntryModuleByIdCache.get(id);
119
+ if (isAwaitingQueue(cacheEntry)) {
120
+ return new Promise((resolve, reject) => {
121
+ cacheEntry.awaitingQueue.push(resolve);
122
+ });
123
+ } else if (cacheEntry) {
124
+ return Promise.resolve(cacheEntry);
125
+ }
126
+ return Promise.resolve(void 0);
127
+ }
128
+ async function setContentEntryModuleCache({
129
+ fileId,
130
+ pluginContext
131
+ }) {
132
+ contentEntryModuleByIdCache.set(fileId, { awaitingQueue: [] });
133
+ const contentConfig = await getContentConfigFromGlobal();
134
+ const rawContents = await fs.promises.readFile(fileId, "utf-8");
135
+ const fileExt = extname(fileId);
136
+ if (!contentEntryExtToParser.has(fileExt)) {
137
+ throw new AstroError({
138
+ ...AstroErrorData.UnknownContentCollectionError,
139
+ message: `No parser found for content entry ${JSON.stringify(
140
+ fileId
141
+ )}. Did you apply an integration for this file type?`
118
142
  });
119
- },
120
- async transform(code, id) {
121
- if (isContentFlagImport(id, contentEntryExts)) {
122
- return { code: escapeViteEnvReferences(code) };
143
+ }
144
+ const contentEntryParser = contentEntryExtToParser.get(fileExt);
145
+ const {
146
+ rawData,
147
+ body,
148
+ slug: unvalidatedSlug,
149
+ data: unvalidatedData
150
+ } = await contentEntryParser.getEntryInfo({
151
+ fileUrl: pathToFileURL(fileId),
152
+ contents: rawContents
153
+ });
154
+ const entryInfoResult = getEntryInfo({
155
+ entry: pathToFileURL(fileId),
156
+ contentDir: contentPaths.contentDir
157
+ });
158
+ if (entryInfoResult instanceof NoCollectionError)
159
+ throw entryInfoResult;
160
+ const { id, slug: generatedSlug, collection } = entryInfoResult;
161
+ const _internal = { filePath: fileId, rawData };
162
+ const slug = getEntrySlug({ id, collection, slug: generatedSlug, unvalidatedSlug });
163
+ const collectionConfig = contentConfig == null ? void 0 : contentConfig.collections[collection];
164
+ let data = collectionConfig ? await getEntryData({ id, collection, slug, _internal, unvalidatedData }, collectionConfig) : unvalidatedData;
165
+ await patchAssets(data, pluginContext.meta.watchMode, pluginContext.emitFile, settings);
166
+ const contentEntryModule = {
167
+ id,
168
+ slug,
169
+ collection,
170
+ data,
171
+ body,
172
+ _internal
173
+ };
174
+ const cacheEntry = contentEntryModuleByIdCache.get(fileId);
175
+ if (isAwaitingQueue(cacheEntry)) {
176
+ for (const resolve of cacheEntry.awaitingQueue) {
177
+ resolve(contentEntryModule);
123
178
  }
124
179
  }
125
- };
180
+ contentEntryModuleByIdCache.set(fileId, contentEntryModule);
181
+ return contentEntryModule;
182
+ }
183
+ return plugins;
184
+ }
185
+ async function getContentConfigFromGlobal() {
186
+ const observable = globalContentConfigObserver.get();
187
+ if (observable.status === "init") {
188
+ throw new AstroError({
189
+ ...AstroErrorData.UnknownContentCollectionError,
190
+ message: "Content config failed to load."
191
+ });
192
+ }
193
+ if (observable.status === "error") {
194
+ throw observable.error;
195
+ }
196
+ let contentConfig = observable.status === "loaded" ? observable.config : void 0;
197
+ if (observable.status === "loading") {
198
+ contentConfig = await new Promise((resolve) => {
199
+ const unsubscribe = globalContentConfigObserver.subscribe((ctx) => {
200
+ if (ctx.status === "loaded") {
201
+ resolve(ctx.config);
202
+ unsubscribe();
203
+ }
204
+ if (ctx.status === "error") {
205
+ resolve(void 0);
206
+ unsubscribe();
207
+ }
208
+ });
209
+ });
210
+ }
211
+ return contentConfig;
126
212
  }
127
213
  export {
128
214
  astroContentImportPlugin
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "2.1.4";
1
+ const ASTRO_VERSION = "2.1.5";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -52,7 +52,7 @@ async function dev(settings, options) {
52
52
  isRestart: options.isRestart
53
53
  })
54
54
  );
55
- const currentVersion = "2.1.4";
55
+ const currentVersion = "2.1.5";
56
56
  if (currentVersion.includes("-")) {
57
57
  warn(options.logging, null, msg.prerelease({ currentVersion }));
58
58
  }
@@ -72,6 +72,7 @@ function enhanceViteSSRError({
72
72
  return safeError;
73
73
  }
74
74
  const ALTERNATIVE_JS_EXTS = ["cjs", "mjs"];
75
+ const ALTERNATIVE_MD_EXTS = ["mdoc"];
75
76
  async function getViteErrorPayload(err) {
76
77
  var _a, _b, _c, _d, _e, _f;
77
78
  let plugin = err.plugin;
@@ -93,6 +94,9 @@ async function getViteErrorPayload(err) {
93
94
  if (ALTERNATIVE_JS_EXTS.includes(highlighterLang ?? "")) {
94
95
  highlighterLang = "js";
95
96
  }
97
+ if (ALTERNATIVE_MD_EXTS.includes(highlighterLang ?? "")) {
98
+ highlighterLang = "md";
99
+ }
96
100
  const highlightedCode = err.fullCode ? highlighter.codeToHtml(err.fullCode, {
97
101
  lang: highlighterLang,
98
102
  lineOptions: ((_c = err.loc) == null ? void 0 : _c.line) ? [{ line: err.loc.line, classes: ["error-line"] }] : void 0
@@ -47,7 +47,7 @@ function serverStart({
47
47
  base,
48
48
  isRestart = false
49
49
  }) {
50
- const version = "2.1.4";
50
+ const version = "2.1.5";
51
51
  const localPrefix = `${dim("\u2503")} Local `;
52
52
  const networkPrefix = `${dim("\u2503")} Network `;
53
53
  const emptyPrefix = " ".repeat(11);
@@ -233,7 +233,7 @@ function printHelp({
233
233
  message.push(
234
234
  linebreak(),
235
235
  ` ${bgGreen(black(` ${commandName} `))} ${green(
236
- `v${"2.1.4"}`
236
+ `v${"2.1.5"}`
237
237
  )} ${headline}`
238
238
  );
239
239
  }
@@ -1,4 +1,3 @@
1
- import fs from "fs";
2
1
  import path from "path";
3
2
  import slash from "slash";
4
3
  const getConfigAlias = (paths, baseUrl) => {
@@ -43,13 +42,14 @@ function configAliasVitePlugin({
43
42
  };
44
43
  }
45
44
  },
46
- resolveId(id) {
47
- if (id.startsWith(".") || id.startsWith("/"))
45
+ async resolveId(id, importer, options) {
46
+ if (id.startsWith(".") || path.isAbsolute(id))
48
47
  return;
49
48
  const resolved = path.posix.join(resolvedBaseUrl, id);
50
- if (fs.existsSync(resolved)) {
51
- return resolved;
52
- }
49
+ return await this.resolve(resolved, importer, {
50
+ skipSelf: true,
51
+ ...options
52
+ });
53
53
  }
54
54
  };
55
55
  }
@@ -5,12 +5,8 @@ import {
5
5
  } from "@astrojs/markdown-remark/dist/internal.js";
6
6
  import fs from "fs";
7
7
  import matter from "gray-matter";
8
- import npath from "node:path";
9
8
  import { fileURLToPath } from "node:url";
10
- import { pathToFileURL } from "url";
11
9
  import { normalizePath } from "vite";
12
- import { imageMetadata } from "../assets/index.js";
13
- import imageSize from "../assets/vendor/image-size/index.js";
14
10
  import { AstroError, AstroErrorData, MarkdownError } from "../core/errors/index.js";
15
11
  import { warn } from "../core/logger/core.js";
16
12
  import { isMarkdownFile } from "../core/util.js";
@@ -42,26 +38,10 @@ function safeMatter(source, id) {
42
38
  const astroJsxRuntimeModulePath = normalizePath(
43
39
  fileURLToPath(new URL("../jsx-runtime/index.js", import.meta.url))
44
40
  );
41
+ const astroServerRuntimeModulePath = normalizePath(
42
+ fileURLToPath(new URL("../runtime/server/index.js", import.meta.url))
43
+ );
45
44
  function markdown({ settings, logging }) {
46
- const markdownAssetMap = /* @__PURE__ */ new Map();
47
- let imageService = void 0;
48
- async function resolveImage(fileId, path) {
49
- const resolved = await this.resolve(path, fileId);
50
- if (!resolved)
51
- return path;
52
- const rel = npath.relative(normalizePath(fileURLToPath(settings.config.root)), resolved.id);
53
- const buffer = await fs.promises.readFile(resolved.id);
54
- if (markdownAssetMap.has(resolved.id)) {
55
- return `ASTRO_ASSET_MD_${markdownAssetMap.get(resolved.id)}`;
56
- }
57
- const file = this.emitFile({
58
- type: "asset",
59
- name: rel,
60
- source: buffer
61
- });
62
- markdownAssetMap.set(resolved.id, file);
63
- return `ASTRO_ASSET_MD_${file}`;
64
- }
65
45
  return {
66
46
  enforce: "pre",
67
47
  name: "astro:markdown",
@@ -74,20 +54,12 @@ function markdown({ settings, logging }) {
74
54
  const { fileId, fileUrl } = getFileInfo(id, settings.config);
75
55
  const rawFile = await fs.promises.readFile(fileId, "utf-8");
76
56
  const raw = safeMatter(rawFile, id);
77
- if (settings.config.experimental.assets) {
78
- imageService = (await import(settings.config.image.service)).default;
79
- }
80
57
  const renderResult = await renderMarkdown(raw.content, {
81
58
  ...settings.config.markdown,
82
59
  fileURL: new URL(`file://${fileId}`),
83
60
  frontmatter: raw.data,
84
- experimentalAssets: settings.config.experimental.assets,
85
- imageService,
86
- assetsDir: new URL("./assets/", settings.config.srcDir),
87
- resolveImage: this.meta.watchMode ? void 0 : resolveImage.bind(this, fileId),
88
- getImageMetadata: imageSize
61
+ experimentalAssets: settings.config.experimental.assets
89
62
  });
90
- this;
91
63
  let html = renderResult.code;
92
64
  const { headings } = renderResult.metadata;
93
65
  let imagePaths = [];
@@ -96,7 +68,10 @@ function markdown({ settings, logging }) {
96
68
  imagePaths = await Promise.all(
97
69
  paths.map(async (imagePath) => {
98
70
  var _a;
99
- return ((_a = await this.resolve(imagePath)) == null ? void 0 : _a.id) ?? imagePath;
71
+ return {
72
+ raw: imagePath,
73
+ absolute: ((_a = await this.resolve(imagePath, id)) == null ? void 0 : _a.id) ?? imagePath
74
+ };
100
75
  })
101
76
  );
102
77
  }
@@ -115,14 +90,25 @@ function markdown({ settings, logging }) {
115
90
  }
116
91
  const code = escapeViteEnvReferences(`
117
92
  import { Fragment, jsx as h } from ${JSON.stringify(astroJsxRuntimeModulePath)};
93
+ import { spreadAttributes } from ${JSON.stringify(astroServerRuntimeModulePath)};
94
+
118
95
  ${layout ? `import Layout from ${JSON.stringify(layout)};` : ""}
119
- ${settings.config.experimental.assets ? 'import { getConfiguredImageService } from "astro:assets";\ngetConfiguredImageService();' : ""}
96
+ ${settings.config.experimental.assets ? 'import { getImage } from "astro:assets";' : ""}
120
97
 
121
- const images = {
122
- ${imagePaths.map((entry) => `'${entry}': await import('${entry}')`)}
98
+ export const images = {
99
+ ${imagePaths.map(
100
+ (entry) => `'${entry.raw}': await getImage({src: (await import("${entry.absolute}")).default})`
101
+ )}
123
102
  }
124
103
 
125
- const html = ${JSON.stringify(html)};
104
+ function updateImageReferences(html) {
105
+ return html.replaceAll(
106
+ /__ASTRO_IMAGE_="(.+)"/gm,
107
+ (full, imagePath) => spreadAttributes({src: images[imagePath].src, ...images[imagePath].attributes})
108
+ );
109
+ }
110
+
111
+ const html = updateImageReferences(${JSON.stringify(html)});
126
112
 
127
113
  export const frontmatter = ${JSON.stringify(frontmatter)};
128
114
  export const file = ${JSON.stringify(fileId)};
@@ -172,31 +158,6 @@ function markdown({ settings, logging }) {
172
158
  }
173
159
  };
174
160
  }
175
- },
176
- async generateBundle(_opts, bundle) {
177
- for (const [, output] of Object.entries(bundle)) {
178
- if (output.type === "asset")
179
- continue;
180
- if (markdownAssetMap.size) {
181
- const optimizedPaths = /* @__PURE__ */ new Map();
182
- for (const [filename, hash] of markdownAssetMap) {
183
- const image = await imageMetadata(pathToFileURL(filename));
184
- if (!image) {
185
- continue;
186
- }
187
- const fileName = this.getFileName(hash);
188
- image.src = npath.join(settings.config.base, fileName);
189
- const options = { src: image };
190
- const validatedOptions = (imageService == null ? void 0 : imageService.validateOptions) ? imageService.validateOptions(options) : options;
191
- const optimized = globalThis.astroAsset.addStaticImage(validatedOptions);
192
- optimizedPaths.set(hash, optimized);
193
- }
194
- output.code = output.code.replaceAll(/ASTRO_ASSET_MD_([0-9a-z]{8})/gm, (_str, hash) => {
195
- const optimizedName = optimizedPaths.get(hash);
196
- return optimizedName || this.getFileName(hash);
197
- });
198
- }
199
- }
200
161
  }
201
162
  };
202
163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -95,7 +95,7 @@
95
95
  "dependencies": {
96
96
  "@astrojs/compiler": "^1.2.0",
97
97
  "@astrojs/language-server": "^0.28.3",
98
- "@astrojs/markdown-remark": "^2.1.1",
98
+ "@astrojs/markdown-remark": "^2.1.2",
99
99
  "@astrojs/telemetry": "^2.1.0",
100
100
  "@astrojs/webapi": "^2.1.0",
101
101
  "@babel/core": "^7.18.2",