Skip to content

Commit fe0a185

Browse files
authored
Import config at build time (#14)
* import config at build time Closes #129 * use console instead of getLogger * only run validation in client compilation pass * move hack outside function call * add comments
1 parent 076771e commit fe0a185

File tree

3 files changed

+88
-54
lines changed

3 files changed

+88
-54
lines changed

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const withMarkdoc =
1313
options: {
1414
...pluginOptions,
1515
dir: options.dir,
16+
nextRuntime: options.nextRuntime,
1617
},
1718
},
1819
],

src/loader.js

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const Markdoc = require('@markdoc/markdoc');
44

55
const DEFAULT_SCHEMA_PATH = './markdoc';
66

7+
function noop() {}
8+
79
function normalize(s) {
810
return s.replace(/\\/g, path.win32.sep.repeat(2));
911
}
@@ -40,7 +42,6 @@ async function gatherPartials(ast, schemaDir) {
4042

4143
// Returning a JSX object is what allows fast refresh to work
4244
async function load(source) {
43-
const logger = this.getLogger('@markdoc/next.js');
4445
// https://webpack.js.org/concepts/module-resolution/
4546
const resolve = this.getResolve({
4647
// https://webpack.js.org/api/loaders/#thisgetresolve
@@ -52,9 +53,11 @@ async function load(source) {
5253
dir, // Root directory from Next.js (contains next.config.js)
5354
mode = 'static',
5455
schemaPath = DEFAULT_SCHEMA_PATH,
56+
nextRuntime,
5557
} = this.getOptions() || {};
5658

5759
const schemaDir = path.resolve(dir, schemaPath || DEFAULT_SCHEMA_PATH);
60+
const ast = Markdoc.parse(source);
5861

5962
// Grabs the path of the file relative to the `/pages` directory
6063
// to pass into the app props later.
@@ -63,59 +66,87 @@ async function load(source) {
6366
// https://nextjs.org/docs/advanced-features/src-directory
6467
const filepath = this.resourcePath.split('pages')[1];
6568

66-
const ast = Markdoc.parse(source);
69+
// Only run validation when during client compilation
70+
if (!nextRuntime) {
71+
let previousRefreshReg = global.$RefreshReg$;
72+
let previousRefreshSig = global.$RefreshSig$;
6773

68-
const errors = Markdoc.validate(ast)
69-
// tags are not yet registered, so ignore these errors
70-
.filter((e) => e.error.id !== 'tag-undefined')
71-
.filter((e) => {
72-
switch (e.error.level) {
73-
case 'debug':
74-
case 'error':
75-
case 'info': {
76-
logger[e.error.level](e.error.message);
77-
break;
78-
}
79-
case 'warning': {
80-
logger.warn(e.error.message);
81-
break;
82-
}
83-
case 'critical': {
84-
logger.error(e.error.message);
85-
break;
74+
// https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/176#issuecomment-683150213
75+
global.$RefreshReg$ = noop;
76+
global.$RefreshSig$ = () => noop;
77+
78+
// This imports the config as an in-memory object
79+
const importAtBuildTime = async (resource) => {
80+
try {
81+
const object = await this.importModule(
82+
await resolve(schemaDir, resource)
83+
);
84+
return object ? object.default || object : {};
85+
} catch (error) {
86+
return undefined;
87+
}
88+
};
89+
90+
const cfg = {
91+
tags: await importAtBuildTime('tags'),
92+
nodes: await importAtBuildTime('nodes'),
93+
functions: await importAtBuildTime('functions'),
94+
...(await importAtBuildTime('config')),
95+
};
96+
97+
const errors = Markdoc.validate(ast, cfg)
98+
.filter((e) => {
99+
switch (e.error.level) {
100+
case 'debug':
101+
case 'error':
102+
case 'info': {
103+
console[e.error.level](e.error.message);
104+
break;
105+
}
106+
case 'warning': {
107+
console.warn(e.error.message);
108+
break;
109+
}
110+
case 'critical': {
111+
console.error(e.error.message);
112+
break;
113+
}
114+
default: {
115+
console.log(e.error.message);
116+
break;
117+
}
86118
}
87-
default: {
88-
logger.log(e.error.message);
89-
break;
119+
return e.error.level === 'critical';
120+
})
121+
.flatMap((e) => {
122+
const lines = source.split('\n');
123+
124+
const message = [e.error.message, ...lines.slice(...e.lines)];
125+
126+
if (
127+
e.error &&
128+
e.error.location &&
129+
e.error.location.start &&
130+
e.error.location.start.offset
131+
) {
132+
const prev = lines.slice(0, e.lines[0]).join('\n').length;
133+
const diff = e.error.location.start.offset - prev;
134+
135+
const pointer = `${' '.repeat(diff)}^`;
136+
message.push(pointer);
90137
}
91-
}
92-
return e.error.level === 'critical';
93-
})
94-
.flatMap((e) => {
95-
const lines = source.split('\n');
96-
97-
const message = [e.error.message, ...lines.slice(...e.lines)];
98-
99-
if (
100-
e.error &&
101-
e.error.location &&
102-
e.error.location.start &&
103-
e.error.location.start.offset
104-
) {
105-
const prev = lines.slice(0, e.lines[0]).join('\n').length;
106-
const diff = e.error.location.start.offset - prev;
107-
108-
const pointer = `${' '.repeat(diff)}^`;
109-
message.push(pointer);
110-
}
111138

112-
// add extra newline between errors
113-
message.push('');
114-
return message;
115-
});
139+
// add extra newline between errors
140+
message.push('');
141+
return message;
142+
});
143+
144+
if (errors.length) {
145+
throw new Error(errors.join('\n'));
146+
}
116147

117-
if (errors.length) {
118-
throw new Error(errors.join('\n'));
148+
global.$RefreshReg$ = previousRefreshReg;
149+
global.$RefreshSig$ = previousRefreshSig;
119150
}
120151

121152
const partials = await gatherPartials.call(
@@ -132,7 +163,8 @@ async function load(source) {
132163
try {
133164
const directoryExists = await fs.promises.stat(schemaDir);
134165

135-
async function readDir(variable) {
166+
// This creates import strings that cause the config to be imported runtime
167+
async function importAtRuntime(variable) {
136168
try {
137169
const module = await resolve(schemaDir, variable);
138170
return `import * as ${variable} from '${normalize(module)}'`;
@@ -143,10 +175,10 @@ async function load(source) {
143175

144176
if (directoryExists) {
145177
schemaCode = `
146-
${await readDir('config')}
147-
${await readDir('tags')}
148-
${await readDir('nodes')}
149-
${await readDir('functions')}
178+
${await importAtRuntime('config')}
179+
${await importAtRuntime('tags')}
180+
${await importAtRuntime('nodes')}
181+
${await importAtRuntime('functions')}
150182
const schema = {
151183
tags: tags ? (tags.default || tags) : {},
152184
nodes: nodes ? (nodes.default || nodes) : {},

tests/index.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function options(config = {}) {
6565
return {
6666
...config,
6767
dir: __dirname,
68+
nextRuntime: 'nodejs',
6869
};
6970
},
7071
getLogger() {

0 commit comments

Comments
 (0)