From 0b4e35e55225565a99ac7fe0570f61b1a4fb891b Mon Sep 17 00:00:00 2001 From: maxblank-stripe <114021544+maxblank-stripe@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:52:53 -0800 Subject: [PATCH] Revert "Enhance Turbopack support and improve loader functionality (#67)" This reverts commit 24c749aa9e152acdd239cacd976728c196cab4ec. --- README.md | 7 --- src/loader.js | 46 +++----------- tests/index.test.js | 148 ++++++++++---------------------------------- 3 files changed, 41 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index 3ccad93..592f515 100644 --- a/README.md +++ b/README.md @@ -32,18 +32,11 @@ The first thing you'll need to do is install `@markdoc/next.js` and add it to yo // next.config.js module.exports = withMarkdoc({ dir: process.cwd(), // Required for Turbopack file resolution - schemaPath: './markdoc', // Wherever your Markdoc schema lives })({ pageExtensions: ['js', 'md'], - turbopack: {}, // Turbopack only runs the loader when a base config exists }); ``` - Turbopack currently requires every schema entry file referenced by `schemaPath` to exist, - even if you are not customizing them yet. Create `config.js`, `nodes.js`, `tags.js`, and - `functions.js` in that directory (exporting empty objects is fine) so the loader can resolve - them during the build. - 3. Create a new Markdoc file in `pages/docs` named `getting-started.md`. ``` diff --git a/src/loader.js b/src/loader.js index b555321..2702550 100644 --- a/src/loader.js +++ b/src/loader.js @@ -8,16 +8,6 @@ function normalize(s) { return s.replace(/\\/g, path.win32.sep.repeat(2)); } -function getRelativeImportPath(from, to) { - const relative = path.relative(path.dirname(from), to); - if (!relative) { - return './'; - } - - const request = relative.startsWith('.') ? relative : `./${relative}`; - return normalize(request); -} - async function gatherPartials(ast, schemaDir, tokenizer, parseOptions) { let partials = {}; @@ -77,7 +67,7 @@ async function load(source) { const ast = Markdoc.parse(tokens, parseOptions); // Determine if this is a page file by checking if it starts with the provided directories - const isPage = (appDir && this.resourcePath.startsWith(appDir)) || + const isPage = (appDir && this.resourcePath.startsWith(appDir)) || (pagesDir && this.resourcePath.startsWith(pagesDir)); // Grabs the path of the file relative to the `/{app,pages}` directory @@ -103,34 +93,14 @@ async function load(source) { const directoryExists = await fs.promises.stat(schemaDir); // This creates import strings that cause the config to be imported runtime - const importAtRuntime = async (variable) => { - const requests = [variable]; - - // Turbopack module resolution currently requires explicit relative paths - // when `preferRelative` is used with bare specifiers (e.g. `tags`). - if ( - typeof variable === 'string' && - !variable.startsWith('.') && - !variable.startsWith('/') - ) { - requests.push(`./${variable}`); + async function importAtRuntime(variable) { + try { + const module = await resolve(schemaDir, variable); + return `import * as ${variable} from '${normalize(module)}'`; + } catch (error) { + return `const ${variable} = {};`; } - - let lastError; - - for (const request of requests) { - try { - const module = await resolve(schemaDir, request); - const modulePath = getRelativeImportPath(this.resourcePath, module); - return `import * as ${variable} from '${modulePath}'`; - } catch (error) { - lastError = error; - } - } - - console.debug('[Markdoc loader] Failed to resolve', { schemaDir, variable, error: lastError }); - return `const ${variable} = {};`; - }; + } if (directoryExists) { schemaCode = ` diff --git a/tests/index.test.js b/tests/index.test.js index 21f3d6f..128b13d 100644 --- a/tests/index.test.js +++ b/tests/index.test.js @@ -2,7 +2,6 @@ const vm = require('vm'); const fs = require('fs'); const path = require('path'); const babel = require('@babel/core'); -const Module = require('module'); const React = require('react'); const enhancedResolve = require('enhanced-resolve'); const loader = require('../src/loader'); @@ -11,8 +10,6 @@ const loader = require('../src/loader'); jest.mock('@markdoc/next.js/runtime', () => require('../src/runtime'), {virtual: true}); const source = fs.readFileSync(require.resolve('./fixture.md'), 'utf-8'); -const consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {}); -const consoleDebugMock = jest.spyOn(console, 'debug').mockImplementation(() => {}); // https://stackoverflow.com/questions/53799385/how-can-i-convert-a-windows-path-to-posix-path-using-node-path function normalizeAbsolutePath(s) { @@ -30,8 +27,12 @@ function normalizeOperatingSystemPaths(s) { .replace(/\/r\/n/g, '\\n'); } -function createRequireContext(requireFn) { - return (base = '.') => { +function evaluate(output) { + const {code} = babel.transformSync(output); + const exports = {}; + + // https://stackoverflow.com/questions/38332094/how-can-i-mock-webpacks-require-context-in-jest + require.context = require.context = (base = '.') => { const files = []; function readDirectory(directory) { @@ -48,56 +49,20 @@ function createRequireContext(requireFn) { readDirectory(path.resolve(__dirname, base)); - return Object.assign(requireFn, {keys: () => files}); - }; -} - -function evaluate(output, filename = path.join(__dirname, 'pages/test/index.md')) { - const {code} = babel.transformSync(output, {filename}); - - const resourceRequire = Module.createRequire(filename); - const baseRequire = require; - - const customRequire = (specifier) => { - if (specifier.startsWith('.') || specifier.startsWith('/')) { - return resourceRequire(specifier); - } - - return baseRequire(specifier); + return Object.assign(require, {keys: () => files}); }; - customRequire.resolve = (specifier) => { - if (specifier.startsWith('.') || specifier.startsWith('/')) { - return resourceRequire.resolve(specifier); - } - - return baseRequire.resolve(specifier); - }; - - customRequire.cache = baseRequire.cache; - customRequire.main = baseRequire.main; - customRequire.extensions = baseRequire.extensions; - customRequire.paths = baseRequire.paths; - - const context = createRequireContext(customRequire); - customRequire.context = context; - require.context = context; - - const exports = {}; - const module = {exports}; - vm.runInNewContext(code, { exports, - module, - require: customRequire, + require, console, }); - return module.exports; + return exports; } function options(config = {}) { - const dir = path.join(__dirname, config.appDir ? 'app' : 'pages'); + const dir = `${'/Users/someone/a-next-js-repo'}/${config.appDir ? 'app' : 'pages'}`; const webpackThis = { context: __dirname, @@ -122,7 +87,7 @@ function options(config = {}) { resolve(context, file, (err, result) => (err ? rej(err) : res(result))) ).then(normalizeAbsolutePath); }, - resourcePath: path.join(dir, 'test', 'index.md'), + resourcePath: dir + '/test/index.md', }; return webpackThis; @@ -152,14 +117,13 @@ test('should fail build if invalid `schemaPath` is used', async () => { }); test('file output is correct', async () => { - const webpackThis = options(); - const output = await callLoader(webpackThis, source); + const output = await callLoader(options(), source); expect(normalizeOperatingSystemPaths(output)).toMatchSnapshot(); - const page = evaluate(output, webpackThis.resourcePath); + const page = evaluate(output); - expect(page).toEqual({ + expect(evaluate(output)).toEqual({ default: expect.any(Function), getStaticProps: expect.any(Function), markdoc: { @@ -198,14 +162,13 @@ test('file output is correct', async () => { }); test('app router', async () => { - const webpackThis = options({appDir: true}); - const output = await callLoader(webpackThis, source); + const output = await callLoader(options({appDir: true}), source); expect(normalizeOperatingSystemPaths(output)).toMatchSnapshot(); - const page = evaluate(output, webpackThis.resourcePath); + const page = evaluate(output); - expect(page).toEqual({ + expect(evaluate(output)).toEqual({ default: expect.any(Function), markdoc: { frontmatter: { @@ -220,9 +183,8 @@ test('app router', async () => { }); test('app router metadata', async () => { - const webpackThis = options({appDir: true}); const output = await callLoader( - webpackThis, + options({appDir: true}), source.replace('---', '---\nmetadata:\n title: Metadata title') ); @@ -237,10 +199,9 @@ test.each([ ['schemas/files', 'markdoc2'], ['schemas/typescript', source], ])('Custom schema path ("%s")', async (schemaPath, expectedChild) => { - const webpackThis = options({schemaPath}); - const output = await callLoader(webpackThis, source); + const output = await callLoader(options({schemaPath}), source); - const page = evaluate(output, webpackThis.resourcePath); + const page = evaluate(output); const data = await page.getStaticProps({}); expect(data.props.markdoc.content.children[0].children[0]).toEqual('Custom title'); @@ -248,67 +209,30 @@ test.each([ }); test('Partials', async () => { - const webpackThis = options({schemaPath: './schemas/partials'}); const output = await callLoader( - webpackThis, + options({schemaPath: './schemas/partials'}), `${source}\n{% partial file="footer.md" /%}` ); - const page = evaluate(output, webpackThis.resourcePath); + const page = evaluate(output); const data = await page.getStaticProps({}); expect(data.props.markdoc.content.children[1].children[0]).toEqual('footer'); }); test('Ejected config', async () => { - const webpackThis = options({schemaPath: './schemas/ejectedConfig'}); const output = await callLoader( - webpackThis, + options({schemaPath: './schemas/ejectedConfig'}), `${source}\n{% $product %}` ); - const page = evaluate(output, webpackThis.resourcePath); + const page = evaluate(output); const data = await page.getStaticProps({}); expect(data.props.markdoc.content.children[1]).toEqual('Extra value'); expect(data.props.markdoc.content.children[2].children[0]).toEqual('meal'); }); -test('falls back to relative schema imports when bare specifiers fail', async () => { - const schemaDir = path.resolve(__dirname, 'schemas/files'); - const resolveRequests = []; - const webpackThis = { - ...options({schemaPath: './schemas/files'}), - }; - - webpackThis.getResolve = () => async (_context, request) => { - resolveRequests.push(request); - const target = { - './tags': path.join(schemaDir, 'tags.js'), - './nodes': path.join(schemaDir, 'nodes.js'), - config: path.join(schemaDir, 'config.js'), - './config': path.join(schemaDir, 'config.js'), - functions: path.join(schemaDir, 'functions.js'), - './functions': path.join(schemaDir, 'functions.js'), - }[request]; - - if (target) { - return normalizeAbsolutePath(target); - } - - throw new Error(`Unable to resolve "${request}"`); - }; - - const output = await callLoader(webpackThis, source); - - expect(resolveRequests).toEqual( - expect.arrayContaining(['tags', './tags', 'nodes', './nodes']) - ); - - const importMatch = output.match(/import \* as tags from '([^']+)'/); - expect(importMatch?.[1].startsWith('.')).toBe(true); -}); - test('HMR', async () => { const output = await callLoader( { @@ -322,10 +246,9 @@ test('HMR', async () => { }); test('mode="server"', async () => { - const webpackThis = options({mode: 'server'}); - const output = await callLoader(webpackThis, source); + const output = await callLoader(options({mode: 'server'}), source); - expect(evaluate(output, webpackThis.resourcePath)).toEqual({ + expect(evaluate(output)).toEqual({ default: expect.any(Function), getServerSideProps: expect.any(Function), markdoc: { @@ -347,7 +270,7 @@ test('import as frontend component', async () => { test('Turbopack configuration', () => { const withMarkdoc = require('../src/index.js'); - + // Test basic Turbopack configuration const config = withMarkdoc()({ pageExtensions: ['js', 'md', 'mdoc'], @@ -355,18 +278,18 @@ test('Turbopack configuration', () => { rules: {}, }, }); - + expect(config.turbopack).toBeDefined(); expect(config.turbopack.rules).toBeDefined(); expect(config.turbopack.rules['*.md']).toBeDefined(); expect(config.turbopack.rules['*.mdoc']).toBeDefined(); - + // Verify rule structure const mdRule = config.turbopack.rules['*.md']; expect(mdRule.loaders).toHaveLength(1); expect(mdRule.loaders[0].loader).toContain('loader'); expect(mdRule.as).toBe('*.js'); - + // Test that existing turbopack config is preserved const configWithExisting = withMarkdoc()({ pageExtensions: ['js', 'md'], @@ -379,10 +302,10 @@ test('Turbopack configuration', () => { }, }, }); - + expect(configWithExisting.turbopack.rules['*.svg']).toBeDefined(); expect(configWithExisting.turbopack.rules['*.md']).toBeDefined(); - + // Test custom extension const configWithCustomExt = withMarkdoc({ extension: /\.(markdown|mdx)$/, @@ -392,12 +315,7 @@ test('Turbopack configuration', () => { rules: {}, }, }); - + expect(configWithCustomExt.turbopack.rules['*.markdown']).toBeDefined(); expect(configWithCustomExt.turbopack.rules['*.mdx']).toBeDefined(); }); - -afterAll(() => { - consoleErrorMock.mockRestore(); - consoleDebugMock.mockRestore(); -});