Skip to content

Commit 0b4e35e

Browse files
Revert "Enhance Turbopack support and improve loader functionality (#67)"
This reverts commit 24c749a.
1 parent 24c749a commit 0b4e35e

File tree

3 files changed

+41
-160
lines changed

3 files changed

+41
-160
lines changed

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,11 @@ The first thing you'll need to do is install `@markdoc/next.js` and add it to yo
3232
// next.config.js
3333
module.exports = withMarkdoc({
3434
dir: process.cwd(), // Required for Turbopack file resolution
35-
schemaPath: './markdoc', // Wherever your Markdoc schema lives
3635
})({
3736
pageExtensions: ['js', 'md'],
38-
turbopack: {}, // Turbopack only runs the loader when a base config exists
3937
});
4038
```
4139

42-
Turbopack currently requires every schema entry file referenced by `schemaPath` to exist,
43-
even if you are not customizing them yet. Create `config.js`, `nodes.js`, `tags.js`, and
44-
`functions.js` in that directory (exporting empty objects is fine) so the loader can resolve
45-
them during the build.
46-
4740
3. Create a new Markdoc file in `pages/docs` named `getting-started.md`.
4841

4942
```

src/loader.js

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@ function normalize(s) {
88
return s.replace(/\\/g, path.win32.sep.repeat(2));
99
}
1010

11-
function getRelativeImportPath(from, to) {
12-
const relative = path.relative(path.dirname(from), to);
13-
if (!relative) {
14-
return './';
15-
}
16-
17-
const request = relative.startsWith('.') ? relative : `./${relative}`;
18-
return normalize(request);
19-
}
20-
2111
async function gatherPartials(ast, schemaDir, tokenizer, parseOptions) {
2212
let partials = {};
2313

@@ -77,7 +67,7 @@ async function load(source) {
7767
const ast = Markdoc.parse(tokens, parseOptions);
7868

7969
// Determine if this is a page file by checking if it starts with the provided directories
80-
const isPage = (appDir && this.resourcePath.startsWith(appDir)) ||
70+
const isPage = (appDir && this.resourcePath.startsWith(appDir)) ||
8171
(pagesDir && this.resourcePath.startsWith(pagesDir));
8272

8373
// Grabs the path of the file relative to the `/{app,pages}` directory
@@ -103,34 +93,14 @@ async function load(source) {
10393
const directoryExists = await fs.promises.stat(schemaDir);
10494

10595
// This creates import strings that cause the config to be imported runtime
106-
const importAtRuntime = async (variable) => {
107-
const requests = [variable];
108-
109-
// Turbopack module resolution currently requires explicit relative paths
110-
// when `preferRelative` is used with bare specifiers (e.g. `tags`).
111-
if (
112-
typeof variable === 'string' &&
113-
!variable.startsWith('.') &&
114-
!variable.startsWith('/')
115-
) {
116-
requests.push(`./${variable}`);
96+
async function importAtRuntime(variable) {
97+
try {
98+
const module = await resolve(schemaDir, variable);
99+
return `import * as ${variable} from '${normalize(module)}'`;
100+
} catch (error) {
101+
return `const ${variable} = {};`;
117102
}
118-
119-
let lastError;
120-
121-
for (const request of requests) {
122-
try {
123-
const module = await resolve(schemaDir, request);
124-
const modulePath = getRelativeImportPath(this.resourcePath, module);
125-
return `import * as ${variable} from '${modulePath}'`;
126-
} catch (error) {
127-
lastError = error;
128-
}
129-
}
130-
131-
console.debug('[Markdoc loader] Failed to resolve', { schemaDir, variable, error: lastError });
132-
return `const ${variable} = {};`;
133-
};
103+
}
134104

135105
if (directoryExists) {
136106
schemaCode = `

tests/index.test.js

Lines changed: 33 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ const vm = require('vm');
22
const fs = require('fs');
33
const path = require('path');
44
const babel = require('@babel/core');
5-
const Module = require('module');
65
const React = require('react');
76
const enhancedResolve = require('enhanced-resolve');
87
const loader = require('../src/loader');
@@ -11,8 +10,6 @@ const loader = require('../src/loader');
1110
jest.mock('@markdoc/next.js/runtime', () => require('../src/runtime'), {virtual: true});
1211

1312
const source = fs.readFileSync(require.resolve('./fixture.md'), 'utf-8');
14-
const consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {});
15-
const consoleDebugMock = jest.spyOn(console, 'debug').mockImplementation(() => {});
1613

1714
// https://stackoverflow.com/questions/53799385/how-can-i-convert-a-windows-path-to-posix-path-using-node-path
1815
function normalizeAbsolutePath(s) {
@@ -30,8 +27,12 @@ function normalizeOperatingSystemPaths(s) {
3027
.replace(/\/r\/n/g, '\\n');
3128
}
3229

33-
function createRequireContext(requireFn) {
34-
return (base = '.') => {
30+
function evaluate(output) {
31+
const {code} = babel.transformSync(output);
32+
const exports = {};
33+
34+
// https://stackoverflow.com/questions/38332094/how-can-i-mock-webpacks-require-context-in-jest
35+
require.context = require.context = (base = '.') => {
3536
const files = [];
3637

3738
function readDirectory(directory) {
@@ -48,56 +49,20 @@ function createRequireContext(requireFn) {
4849

4950
readDirectory(path.resolve(__dirname, base));
5051

51-
return Object.assign(requireFn, {keys: () => files});
52-
};
53-
}
54-
55-
function evaluate(output, filename = path.join(__dirname, 'pages/test/index.md')) {
56-
const {code} = babel.transformSync(output, {filename});
57-
58-
const resourceRequire = Module.createRequire(filename);
59-
const baseRequire = require;
60-
61-
const customRequire = (specifier) => {
62-
if (specifier.startsWith('.') || specifier.startsWith('/')) {
63-
return resourceRequire(specifier);
64-
}
65-
66-
return baseRequire(specifier);
52+
return Object.assign(require, {keys: () => files});
6753
};
6854

69-
customRequire.resolve = (specifier) => {
70-
if (specifier.startsWith('.') || specifier.startsWith('/')) {
71-
return resourceRequire.resolve(specifier);
72-
}
73-
74-
return baseRequire.resolve(specifier);
75-
};
76-
77-
customRequire.cache = baseRequire.cache;
78-
customRequire.main = baseRequire.main;
79-
customRequire.extensions = baseRequire.extensions;
80-
customRequire.paths = baseRequire.paths;
81-
82-
const context = createRequireContext(customRequire);
83-
customRequire.context = context;
84-
require.context = context;
85-
86-
const exports = {};
87-
const module = {exports};
88-
8955
vm.runInNewContext(code, {
9056
exports,
91-
module,
92-
require: customRequire,
57+
require,
9358
console,
9459
});
9560

96-
return module.exports;
61+
return exports;
9762
}
9863

9964
function options(config = {}) {
100-
const dir = path.join(__dirname, config.appDir ? 'app' : 'pages');
65+
const dir = `${'/Users/someone/a-next-js-repo'}/${config.appDir ? 'app' : 'pages'}`;
10166

10267
const webpackThis = {
10368
context: __dirname,
@@ -122,7 +87,7 @@ function options(config = {}) {
12287
resolve(context, file, (err, result) => (err ? rej(err) : res(result)))
12388
).then(normalizeAbsolutePath);
12489
},
125-
resourcePath: path.join(dir, 'test', 'index.md'),
90+
resourcePath: dir + '/test/index.md',
12691
};
12792

12893
return webpackThis;
@@ -152,14 +117,13 @@ test('should fail build if invalid `schemaPath` is used', async () => {
152117
});
153118

154119
test('file output is correct', async () => {
155-
const webpackThis = options();
156-
const output = await callLoader(webpackThis, source);
120+
const output = await callLoader(options(), source);
157121

158122
expect(normalizeOperatingSystemPaths(output)).toMatchSnapshot();
159123

160-
const page = evaluate(output, webpackThis.resourcePath);
124+
const page = evaluate(output);
161125

162-
expect(page).toEqual({
126+
expect(evaluate(output)).toEqual({
163127
default: expect.any(Function),
164128
getStaticProps: expect.any(Function),
165129
markdoc: {
@@ -198,14 +162,13 @@ test('file output is correct', async () => {
198162
});
199163

200164
test('app router', async () => {
201-
const webpackThis = options({appDir: true});
202-
const output = await callLoader(webpackThis, source);
165+
const output = await callLoader(options({appDir: true}), source);
203166

204167
expect(normalizeOperatingSystemPaths(output)).toMatchSnapshot();
205168

206-
const page = evaluate(output, webpackThis.resourcePath);
169+
const page = evaluate(output);
207170

208-
expect(page).toEqual({
171+
expect(evaluate(output)).toEqual({
209172
default: expect.any(Function),
210173
markdoc: {
211174
frontmatter: {
@@ -220,9 +183,8 @@ test('app router', async () => {
220183
});
221184

222185
test('app router metadata', async () => {
223-
const webpackThis = options({appDir: true});
224186
const output = await callLoader(
225-
webpackThis,
187+
options({appDir: true}),
226188
source.replace('---', '---\nmetadata:\n title: Metadata title')
227189
);
228190

@@ -237,78 +199,40 @@ test.each([
237199
['schemas/files', 'markdoc2'],
238200
['schemas/typescript', source],
239201
])('Custom schema path ("%s")', async (schemaPath, expectedChild) => {
240-
const webpackThis = options({schemaPath});
241-
const output = await callLoader(webpackThis, source);
202+
const output = await callLoader(options({schemaPath}), source);
242203

243-
const page = evaluate(output, webpackThis.resourcePath);
204+
const page = evaluate(output);
244205

245206
const data = await page.getStaticProps({});
246207
expect(data.props.markdoc.content.children[0].children[0]).toEqual('Custom title');
247208
expect(data.props.markdoc.content.children[1]).toEqual(expectedChild);
248209
});
249210

250211
test('Partials', async () => {
251-
const webpackThis = options({schemaPath: './schemas/partials'});
252212
const output = await callLoader(
253-
webpackThis,
213+
options({schemaPath: './schemas/partials'}),
254214
`${source}\n{% partial file="footer.md" /%}`
255215
);
256216

257-
const page = evaluate(output, webpackThis.resourcePath);
217+
const page = evaluate(output);
258218

259219
const data = await page.getStaticProps({});
260220
expect(data.props.markdoc.content.children[1].children[0]).toEqual('footer');
261221
});
262222

263223
test('Ejected config', async () => {
264-
const webpackThis = options({schemaPath: './schemas/ejectedConfig'});
265224
const output = await callLoader(
266-
webpackThis,
225+
options({schemaPath: './schemas/ejectedConfig'}),
267226
`${source}\n{% $product %}`
268227
);
269228

270-
const page = evaluate(output, webpackThis.resourcePath);
229+
const page = evaluate(output);
271230

272231
const data = await page.getStaticProps({});
273232
expect(data.props.markdoc.content.children[1]).toEqual('Extra value');
274233
expect(data.props.markdoc.content.children[2].children[0]).toEqual('meal');
275234
});
276235

277-
test('falls back to relative schema imports when bare specifiers fail', async () => {
278-
const schemaDir = path.resolve(__dirname, 'schemas/files');
279-
const resolveRequests = [];
280-
const webpackThis = {
281-
...options({schemaPath: './schemas/files'}),
282-
};
283-
284-
webpackThis.getResolve = () => async (_context, request) => {
285-
resolveRequests.push(request);
286-
const target = {
287-
'./tags': path.join(schemaDir, 'tags.js'),
288-
'./nodes': path.join(schemaDir, 'nodes.js'),
289-
config: path.join(schemaDir, 'config.js'),
290-
'./config': path.join(schemaDir, 'config.js'),
291-
functions: path.join(schemaDir, 'functions.js'),
292-
'./functions': path.join(schemaDir, 'functions.js'),
293-
}[request];
294-
295-
if (target) {
296-
return normalizeAbsolutePath(target);
297-
}
298-
299-
throw new Error(`Unable to resolve "${request}"`);
300-
};
301-
302-
const output = await callLoader(webpackThis, source);
303-
304-
expect(resolveRequests).toEqual(
305-
expect.arrayContaining(['tags', './tags', 'nodes', './nodes'])
306-
);
307-
308-
const importMatch = output.match(/import \* as tags from '([^']+)'/);
309-
expect(importMatch?.[1].startsWith('.')).toBe(true);
310-
});
311-
312236
test('HMR', async () => {
313237
const output = await callLoader(
314238
{
@@ -322,10 +246,9 @@ test('HMR', async () => {
322246
});
323247

324248
test('mode="server"', async () => {
325-
const webpackThis = options({mode: 'server'});
326-
const output = await callLoader(webpackThis, source);
249+
const output = await callLoader(options({mode: 'server'}), source);
327250

328-
expect(evaluate(output, webpackThis.resourcePath)).toEqual({
251+
expect(evaluate(output)).toEqual({
329252
default: expect.any(Function),
330253
getServerSideProps: expect.any(Function),
331254
markdoc: {
@@ -347,26 +270,26 @@ test('import as frontend component', async () => {
347270

348271
test('Turbopack configuration', () => {
349272
const withMarkdoc = require('../src/index.js');
350-
273+
351274
// Test basic Turbopack configuration
352275
const config = withMarkdoc()({
353276
pageExtensions: ['js', 'md', 'mdoc'],
354277
turbopack: {
355278
rules: {},
356279
},
357280
});
358-
281+
359282
expect(config.turbopack).toBeDefined();
360283
expect(config.turbopack.rules).toBeDefined();
361284
expect(config.turbopack.rules['*.md']).toBeDefined();
362285
expect(config.turbopack.rules['*.mdoc']).toBeDefined();
363-
286+
364287
// Verify rule structure
365288
const mdRule = config.turbopack.rules['*.md'];
366289
expect(mdRule.loaders).toHaveLength(1);
367290
expect(mdRule.loaders[0].loader).toContain('loader');
368291
expect(mdRule.as).toBe('*.js');
369-
292+
370293
// Test that existing turbopack config is preserved
371294
const configWithExisting = withMarkdoc()({
372295
pageExtensions: ['js', 'md'],
@@ -379,10 +302,10 @@ test('Turbopack configuration', () => {
379302
},
380303
},
381304
});
382-
305+
383306
expect(configWithExisting.turbopack.rules['*.svg']).toBeDefined();
384307
expect(configWithExisting.turbopack.rules['*.md']).toBeDefined();
385-
308+
386309
// Test custom extension
387310
const configWithCustomExt = withMarkdoc({
388311
extension: /\.(markdown|mdx)$/,
@@ -392,12 +315,7 @@ test('Turbopack configuration', () => {
392315
rules: {},
393316
},
394317
});
395-
318+
396319
expect(configWithCustomExt.turbopack.rules['*.markdown']).toBeDefined();
397320
expect(configWithCustomExt.turbopack.rules['*.mdx']).toBeDefined();
398321
});
399-
400-
afterAll(() => {
401-
consoleErrorMock.mockRestore();
402-
consoleDebugMock.mockRestore();
403-
});

0 commit comments

Comments
 (0)