Skip to content
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
104 changes: 104 additions & 0 deletions bin/packages/build-v2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import watch from 'node-watch';
// See https://github.com/WordPress/gutenberg/issues/72136
// eslint-disable-next-line import/no-unresolved
import browserslistToEsbuild from 'browserslist-to-esbuild';
import { sassPlugin } from 'esbuild-sass-plugin';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
import rtlcss from 'rtlcss';

/**
* Internal dependencies
Expand Down Expand Up @@ -513,6 +517,33 @@ async function bundlePackage( packageName ) {
}
}

// Copy CSS files from build-style to build directory (for wpScript packages)
if ( packageJson.wpScript ) {
const buildStyleDir = path.join( packageDir, 'build-style' );
const outputDir = path.join( PACKAGES_DIR, '..', 'build', packageName );

try {
// Find CSS files in build-style directory
const cssFiles = await glob(
normalizePath( path.join( buildStyleDir, '*.css' ) )
);

if ( cssFiles.length > 0 ) {
// Ensure output directory exists
await mkdir( outputDir, { recursive: true } );

// Copy each CSS file
for ( const cssFile of cssFiles ) {
const filename = path.basename( cssFile );
const destPath = path.join( outputDir, filename );
builds.push( copyFile( cssFile, destPath ) );
}
}
} catch ( error ) {
// build-style doesn't exist or is empty - that's fine, not all packages have styles
}
}

if ( builds.length === 0 ) {
return false;
}
Expand Down Expand Up @@ -625,11 +656,84 @@ async function transpilePackage( packageName ) {
}
}

await compileStyles( packageName );

await Promise.all( builds );

return Date.now() - startTime;
}

/**
* Compile styles for a single package.
*
* @param {string} packageName Package name.
* @return {Promise<number|null>} Build time in milliseconds, or null if no styles.
*/
async function compileStyles( packageName ) {
const packageDir = path.join( PACKAGES_DIR, packageName );
const styleEntryPath = path.join( packageDir, 'src', 'style.scss' );

// Check if style entry point exists
try {
await readFile( styleEntryPath );
} catch {
return null;
}

const startTime = Date.now();
const buildStyleDir = path.join( packageDir, 'build-style' );

// Create build-style directory
await mkdir( buildStyleDir, { recursive: true } );

// Build with Sass plugin
await esbuild.build( {
entryPoints: [ styleEntryPath ],
outdir: buildStyleDir,
bundle: true,
write: false,
loader: {
'.scss': 'css',
},
plugins: [
sassPlugin( {
Copy link
Member

@aduth aduth Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esbuild-sass-plugin disables embedded setting by default, which is where we'd get a huge speed boost by opting into using sass-embedded package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with this, can you explain more, is it just a flag to make things faster? or does it have other impacts?

Copy link
Member

@aduth aduth Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it just a flag to make things faster?

Essentially, yeah 😄 It requires sass-embedded as an additional peer dependency (official Sass package), but otherwise should act the same the equivalent JavaScript-based Sass Dart compiler.

Copy link
Member

@aduth aduth Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in #72258

Separately, one of the other Sass optimizations I might explore is using the Shared Resource API, which keeps a warm cache between Sass builds which use common resources, something which we definitely do and would benefit (e.g. base styles). Anecdotally in another project, I've seen a stacking 50% reduction and 65% reduction in compile times between enabling sass-embedded and Shared Resource API respectively [1][2].

loadPaths: [
'node_modules',
path.join( PACKAGES_DIR, 'base-styles' ),
],
async transform( source ) {
// Process with autoprefixer for LTR version
const ltrResult = await postcss( [
autoprefixer( { grid: true } ),
] ).process( source, { from: undefined } );

// Process with rtlcss for RTL version
const rtlResult = await postcss( [ rtlcss() ] ).process(
ltrResult.css,
{ from: undefined }
);

// Write both versions
await Promise.all( [
writeFile(
path.join( buildStyleDir, 'style.css' ),
ltrResult.css
),
writeFile(
path.join( buildStyleDir, 'style-rtl.css' ),
rtlResult.css
),
] );

return '';
},
} ),
],
} );

return Date.now() - startTime;
}

/**
* Determine if a file is a source file in a v2 package.
*
Expand Down
3 changes: 3 additions & 0 deletions bin/packages/v2-packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const V2_PACKAGES = [
'blob',
'block-serialization-default-parser',
'blocks',
'components',
'compose',
'data',
'data-controls',
Expand All @@ -25,6 +26,7 @@ const V2_PACKAGES = [
'hooks',
'html-entities',
'i18n',
'icons',
'interactivity',
'interactivity-router',
'is-shallow-equal',
Expand All @@ -35,6 +37,7 @@ const V2_PACKAGES = [
'media-utils',
'notices',
'preferences-persistence',
'primitives',
'priority-queue',
'private-apis',
'react-i18n',
Expand Down
Loading
Loading