Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix source maps generation for the UMD build. #991

Merged
merged 4 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions packages/ckeditor5-dev-build-tools/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import util from 'util';
import chalk from 'chalk';
import path from 'upath';
import { rollup, type RollupOutput, type GlobalsOption } from 'rollup';
import { loadSourcemaps } from './plugins/loadSourcemaps.js';
import { getRollupConfig } from './config.js';
import { getCwdPath, camelizeObjectKeys, removeWhitespace } from './utils.js';
import { getCwdPath, camelizeObjectKeys, removeWhitespace, getOptionalPlugin } from './utils.js';

export interface BuildOptions {
input: string;
Expand Down Expand Up @@ -104,19 +105,33 @@ async function generateUmdBuild( args: BuildOptions, bundle: RollupOutput ): Pro

const { dir, name } = path.parse( args.output );
const { plugins, ...config } = await getRollupConfig( args );
const build = await rollup( config );
const globals = {
...CKEDITOR_GLOBALS,
...args.globals as GlobalsOption
};

/**
* Ignore the plugins we used for the ESM build. Instead, add a new plugin to not only
* load the source code of the dependencies (which is the default in Rollup for better
* performance), but also their source maps to generate a proper final source map for
* the UMD bundle.
*/
const build = await rollup( {
...config,
plugins: [
getOptionalPlugin(
args.sourceMap,
loadSourcemaps()
)
]
} );

const umdBundle = await build.write( {
format: 'umd',
file: path.join( dir, `${ name }.umd.js` ),
assetFileNames: '[name][extname]',
sourcemap: args.sourceMap,
name: args.name,
globals
globals: {
...CKEDITOR_GLOBALS,
...args.globals as GlobalsOption
}
} );

return {
Expand Down
11 changes: 2 additions & 9 deletions packages/ckeditor5-dev-build-tools/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import path from 'upath';
import { existsSync } from 'fs';
import { getUserDependency } from './utils.js';
import { getOptionalPlugin, getUserDependency } from './utils.js';
import type { PackageJson } from 'type-fest';
import type { InputPluginOption, Plugin, RollupOptions } from 'rollup';
import type { Plugin, RollupOptions } from 'rollup';
import type { BuildOptions } from './build.js';

/**
Expand Down Expand Up @@ -272,13 +272,6 @@ export async function getRollupConfig( options: BuildOptions ) {
} as const satisfies RollupOptions;
}

/**
* Returns plugin if condition is truthy. This is used only to get the types right.
*/
function getOptionalPlugin<T extends InputPluginOption>( condition: unknown, plugin: T ): T | undefined {
return condition ? plugin : undefined;
}

/**
* Returns a list of keys in `package.json` file of a given dependency.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/ckeditor5-dev-build-tools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
export { build } from './build.js';
export { addBanner, type RollupBannerOptions } from './plugins/banner.js';
export { emitCss, type RollupEmitCssOptions } from './plugins/emitCss.js';
export { loadSourcemaps } from './plugins/loadSourcemaps.js';
export { replaceImports, type RollupReplaceOptions } from './plugins/replace.js';
export { splitCss, type RollupSplitCssOptions } from './plugins/splitCss.js';
export { translations, type RollupTranslationsOptions } from './plugins/translations.js';
25 changes: 25 additions & 0 deletions packages/ckeditor5-dev-build-tools/src/plugins/loadSourcemaps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import fs from 'fs';
import type { Plugin } from 'rollup';

export function loadSourcemaps(): Plugin {
return {
name: 'cke5-load-sourcemaps',
load( id: string ) {
const sourceMapId = id + '.map';

if ( !fs.existsSync( sourceMapId ) ) {
return;
}

return {
code: fs.readFileSync( id, 'utf-8' ),
map: fs.readFileSync( sourceMapId, 'utf-8' )
};
}
};
}
8 changes: 8 additions & 0 deletions packages/ckeditor5-dev-build-tools/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { createRequire } from 'module';
import path from 'upath';
import type { CamelCase, CamelCasedProperties } from 'type-fest';
import type { InputPluginOption } from 'rollup';

const require = createRequire( import.meta.url );

Expand Down Expand Up @@ -60,3 +61,10 @@ export function getUserDependency( name: string ): any {

return require( path );
}

/**
* Returns plugin if condition is truthy. This is used only to get the types right.
*/
export function getOptionalPlugin<T extends InputPluginOption>( condition: unknown, plugin: T ): T | undefined {
return condition ? plugin : undefined;
}
20 changes: 19 additions & 1 deletion packages/ckeditor5-dev-build-tools/tests/_utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*/

import { expect, vi } from 'vitest';
import type { RollupOutput, OutputChunk, OutputAsset } from 'rollup';
import swc from '@rollup/plugin-swc';
import type { RollupOutput, OutputChunk, OutputAsset, Plugin } from 'rollup';
import * as utils from '../../src/utils';

/**
Expand Down Expand Up @@ -71,3 +72,20 @@ export async function mockGetUserDependency( path: string, cb: () => any ): Prom
return actualImport( url );
} );
}

/**
* Helper function for getting a preconfigured `swc` plugin.
*/
export function swcPlugin(): Plugin {
return swc( {
include: [ '**/*.[jt]s' ],
swc: {
jsc: {
target: 'es2019'
},
module: {
type: 'es6'
}
}
} );
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@

import { join } from 'path';
import { test, expect } from 'vitest';
import swc from '@rollup/plugin-swc';
import styles from 'rollup-plugin-styles';
import { rollup, type RollupOutput, type OutputAsset } from 'rollup';
import { verifyAsset, verifyChunk } from '../../_utils/utils.js';
import { swcPlugin, verifyAsset, verifyChunk } from '../../_utils/utils.js';

import { addBanner, type RollupBannerOptions } from '../../../src/index.js';

Expand All @@ -19,17 +18,7 @@ async function generateBundle( options: RollupBannerOptions, sourcemap: boolean
const bundle = await rollup( {
input: join( import.meta.dirname, './fixtures/input.ts' ),
plugins: [
swc( {
include: [ '**/*.[jt]s' ],
swc: {
jsc: {
target: 'es2019'
},
module: {
type: 'es6'
}
}
} ),
swcPlugin,

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,17 @@

import { join } from 'path';
import { test } from 'vitest';
import swc from '@rollup/plugin-swc';
import styles from 'rollup-plugin-styles';
import { rollup, type RollupOutput } from 'rollup';
import { verifyAsset } from '../../_utils/utils.js';
import { swcPlugin, verifyAsset } from '../../_utils/utils.js';

import { emitCss } from '../../../src/index.js';

async function generateBundle( input: string ): Promise<RollupOutput['output']> {
const bundle = await rollup( {
input: join( import.meta.dirname, input ),
plugins: [
swc( {
include: [ '**/*.[jt]s' ],
swc: {
jsc: {
target: 'es2019'
},
module: {
type: 'es6'
}
}
} ),
swcPlugin,

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

// The `magic-string` package is used because it contains source maps.
export { default as MagicString } from 'magic-string';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": [
"**/*"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

import { join } from 'path';
import { test, expect } from 'vitest';
import { rollup, type RollupOutput, type OutputAsset } from 'rollup';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import { swcPlugin } from '../../_utils/utils.js';

import { loadSourcemaps } from '../../../src/index.js';
import { getOptionalPlugin } from '../../../src/utils.js';

async function generateBundle( input: string, sourcemap: boolean = false ): Promise<RollupOutput[ 'output' ]> {
const bundle = await rollup( {
input: join( import.meta.dirname, input ),
plugins: [
nodeResolve(),
swcPlugin,
getOptionalPlugin( sourcemap, loadSourcemaps() )
]
} );

const { output } = await bundle.generate( {
format: 'esm',
file: 'input.js',
assetFileNames: '[name][extname]',
sourcemap
} );

return output;
}

test( 'Emits source maps combined with source maps of dependencies', async () => {
const output = await generateBundle( './fixtures/input.ts', true );
const sourceMap = output.find( asset => asset.fileName === 'input.js.map' ) as OutputAsset;

/**
* The resulting source map will only contain the `/magic-string/src/` string if the source map
* of the `magic-string` dependency was loaded and combined with the source map of the input file.
* Otherwise, the source map will contain the `/magic-string/dist/` string, which is the bundled
* build of the `magic-string` dependency.
*/
expect( sourceMap.source ).toContain( '/magic-string/src/' );
} );