refactor[ci/build]: preserve header format in artifacts (#27671)

In order to make Haste work with React's artifacts, It is important to
keep headers in this format:
```
/**
* ...
...
* ...
*/
```

For optimization purposes, Closure compiler will actually modify these
headers by removing * prefixes, which is expected.
We should pass sources to the compiler without license headers, with
these changes the current flow will be:
1. Apply top-level definitions. For UMD-bundles, for example, or
DEV-only bundles (e. g. `if (__DEV__) { ...`)
2. Apply licence headers for artifacts with sourcemaps: oss-production
and oss-profiling bundles, they don't need to preserve the header format
to comply with Haste. We need to apply these headers before passing
sources to Closure, so it can build correct mappings for sourcemaps.
3. Pass these sources to closure compiler for minification and
sourcemaps building.
4. Apply licence headers for artifacts without sourcemaps: dev bundles,
fb bundles. This way the header style will be preserved and not changed
by Closure.
This commit is contained in:
Ruslan Lesiutin
2023-11-09 16:00:21 +00:00
committed by GitHub
parent 7bdd7cc2d8
commit c47c306a7a
2 changed files with 246 additions and 113 deletions

View File

@@ -470,11 +470,10 @@ function getPlugins(
// I'm going to port "art" to ES modules to avoid this problem.
// Please don't enable this for anything else!
isUMDBundle && entry === 'react-art' && commonjs(),
// License and haste headers, top-level `if` blocks.
{
name: 'license-and-headers',
name: 'top-level-definitions',
renderChunk(source) {
return Wrappers.wrapBundle(
return Wrappers.wrapWithTopLevelDefinitions(
source,
bundleType,
globalName,
@@ -484,6 +483,21 @@ function getPlugins(
);
},
},
// License and haste headers for artifacts with sourcemaps
// For artifacts with sourcemaps we apply these headers
// before passing sources to the Closure compiler, which will be building sourcemaps
needsSourcemaps && {
name: 'license-and-signature-header-for-artifacts-with-sourcemaps',
renderChunk(source) {
return Wrappers.wrapWithLicenseHeader(
source,
bundleType,
globalName,
filename,
moduleType
);
},
},
// Apply dead code elimination and/or minification.
// closure doesn't yet support leaving ESM imports intact
needsMinifiedByClosure &&
@@ -527,7 +541,7 @@ function getPlugins(
}),
needsSourcemaps && {
name: 'generate-prod-bundle-sourcemaps',
async renderChunk(codeAfterLicense, chunk, options, meta) {
async renderChunk(minifiedCodeWithChangedHeader, chunk, options, meta) {
// We want to generate a sourcemap that shows the production bundle source
// as it existed before Closure Compiler minified that chunk, rather than
// showing the "original" individual source files. This better shows
@@ -583,7 +597,7 @@ function getPlugins(
// Add the sourcemap URL to the actual bundle, so that tools pick it up
const sourceWithMappingUrl =
codeAfterLicense +
minifiedCodeWithChangedHeader +
`\n//# sourceMappingURL=${finalSourcemapFilename}`;
return {
@@ -592,6 +606,21 @@ function getPlugins(
};
},
},
// License and haste headers for artifacts without sourcemaps
// Primarily used for FB-artifacts, which should preserve specific format of the header
// Which potentially can be changed by Closure minification
!needsSourcemaps && {
name: 'license-and-signature-header-for-artifacts-without-sourcemaps',
renderChunk(source) {
return Wrappers.wrapWithLicenseHeader(
source,
bundleType,
globalName,
filename,
moduleType
);
},
},
// Record bundle size.
sizes({
getSize: (size, gzip) => {

View File

@@ -53,7 +53,178 @@ const license = ` * Copyright (c) Meta Platforms, Inc. and affiliates.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.`;
const wrappers = {
const topLevelDefinitionWrappers = {
/***************** NODE_ES2015 *****************/
[NODE_ES2015](source, globalName, filename, moduleType) {
return `'use strict';
${source}`;
},
/***************** ESM_DEV *****************/
[ESM_DEV](source, globalName, filename, moduleType) {
return source;
},
/***************** ESM_PROD *****************/
[ESM_PROD](source, globalName, filename, moduleType) {
return source;
},
/***************** BUN_DEV *****************/
[BUN_DEV](source, globalName, filename, moduleType) {
return source;
},
/***************** BUN_PROD *****************/
[BUN_PROD](source, globalName, filename, moduleType) {
return source;
},
/***************** UMD_DEV *****************/
[UMD_DEV](source, globalName, filename, moduleType) {
return source;
},
/***************** UMD_PROD *****************/
[UMD_PROD](source, globalName, filename, moduleType) {
return `(function(){${source}})();`;
},
/***************** UMD_PROFILING *****************/
[UMD_PROFILING](source, globalName, filename, moduleType) {
return `(function(){${source}})();`;
},
/***************** NODE_DEV *****************/
[NODE_DEV](source, globalName, filename, moduleType) {
return `'use strict';
if (process.env.NODE_ENV !== "production") {
(function() {
${source}
})();
}`;
},
/***************** NODE_PROD *****************/
[NODE_PROD](source, globalName, filename, moduleType) {
return source;
},
/***************** NODE_PROFILING *****************/
[NODE_PROFILING](source, globalName, filename, moduleType) {
return source;
},
/****************** FB_WWW_DEV ******************/
[FB_WWW_DEV](source, globalName, filename, moduleType) {
return `'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`;
},
/****************** FB_WWW_PROD ******************/
[FB_WWW_PROD](source, globalName, filename, moduleType) {
return source;
},
/****************** FB_WWW_PROFILING ******************/
[FB_WWW_PROFILING](source, globalName, filename, moduleType) {
return source;
},
/****************** RN_OSS_DEV ******************/
[RN_OSS_DEV](source, globalName, filename, moduleType) {
return `'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`;
},
/****************** RN_OSS_PROD ******************/
[RN_OSS_PROD](source, globalName, filename, moduleType) {
return source;
},
/****************** RN_OSS_PROFILING ******************/
[RN_OSS_PROFILING](source, globalName, filename, moduleType) {
return source;
},
/****************** RN_FB_DEV ******************/
[RN_FB_DEV](source, globalName, filename, moduleType) {
return `'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`;
},
/****************** RN_FB_PROD ******************/
[RN_FB_PROD](source, globalName, filename, moduleType) {
return source;
},
/****************** RN_FB_PROFILING ******************/
[RN_FB_PROFILING](source, globalName, filename, moduleType) {
return source;
},
};
const reconcilerWrappers = {
/***************** NODE_DEV (reconciler only) *****************/
[NODE_DEV](source, globalName, filename, moduleType) {
return `'use strict';
if (process.env.NODE_ENV !== "production") {
module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
}
`;
},
/***************** NODE_PROD (reconciler only) *****************/
[NODE_PROD](source, globalName, filename, moduleType) {
return `module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
`;
},
/***************** NODE_PROFILING (reconciler only) *****************/
[NODE_PROFILING](source, globalName, filename, moduleType) {
return `module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
`;
},
};
const licenseHeaderWrappers = {
/***************** NODE_ES2015 *****************/
[NODE_ES2015](source, globalName, filename, moduleType) {
return `/**
@@ -63,8 +234,6 @@ const wrappers = {
${license}
*/
'use strict';
${source}`;
},
@@ -107,7 +276,7 @@ ${source}`;
/***************** BUN_PROD *****************/
[BUN_PROD](source, globalName, filename, moduleType) {
return `/**
* @license React
* @license React
* ${filename}
*
${license}
@@ -124,6 +293,7 @@ ${source}`;
*
${license}
*/
${source}`;
},
@@ -135,7 +305,8 @@ ${source}`;
*
${license}
*/
(function(){${source}})();`;
${source}`;
},
/***************** UMD_PROFILING *****************/
@@ -146,7 +317,8 @@ ${license}
*
${license}
*/
(function(){${source}})();`;
${source}`;
},
/***************** NODE_DEV *****************/
@@ -158,13 +330,7 @@ ${license}
${license}
*/
'use strict';
if (process.env.NODE_ENV !== "production") {
(function() {
${source}
})();
}`;
${source}`;
},
/***************** NODE_PROD *****************/
@@ -175,6 +341,7 @@ ${source}
*
${license}
*/
${source}`;
},
@@ -186,13 +353,13 @@ ${source}`;
*
${license}
*/
${source}`;
},
/****************** FB_WWW_DEV ******************/
[FB_WWW_DEV](source, globalName, filename, moduleType) {
return `/**
* @preserve
${license}
*
* @noflow
@@ -201,19 +368,12 @@ ${license}
* @preserve-invariant-messages
*/
'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`;
${source}`;
},
/****************** FB_WWW_PROD ******************/
[FB_WWW_PROD](source, globalName, filename, moduleType) {
return `/**
* @preserve
${license}
*
* @noflow
@@ -228,7 +388,6 @@ ${source}`;
/****************** FB_WWW_PROFILING ******************/
[FB_WWW_PROFILING](source, globalName, filename, moduleType) {
return `/**
* @preserve
${license}
*
* @noflow
@@ -243,7 +402,6 @@ ${source}`;
/****************** RN_OSS_DEV ******************/
[RN_OSS_DEV](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -253,19 +411,12 @@ ${license}
* ${getSigningToken()}
*/
'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`);
${source}`);
},
/****************** RN_OSS_PROD ******************/
[RN_OSS_PROD](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -281,7 +432,6 @@ ${source}`);
/****************** RN_OSS_PROFILING ******************/
[RN_OSS_PROFILING](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -297,7 +447,6 @@ ${source}`);
/****************** RN_FB_DEV ******************/
[RN_FB_DEV](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -306,19 +455,12 @@ ${license}
* ${getSigningToken()}
*/
'use strict';
if (__DEV__) {
(function() {
${source}
})();
}`);
${source}`);
},
/****************** RN_FB_PROD ******************/
[RN_FB_PROD](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -333,7 +475,6 @@ ${source}`);
/****************** RN_FB_PROFILING ******************/
[RN_FB_PROFILING](source, globalName, filename, moduleType) {
return signFile(`/**
* @preserve
${license}
*
* @noflow
@@ -346,69 +487,7 @@ ${source}`);
},
};
const reconcilerWrappers = {
/***************** NODE_DEV (reconciler only) *****************/
[NODE_DEV](source, globalName, filename, moduleType) {
return `/**
* @license React
* ${filename}
*
${license}
*/
'use strict';
if (process.env.NODE_ENV !== "production") {
module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
}
`;
},
/***************** NODE_PROD (reconciler only) *****************/
[NODE_PROD](source, globalName, filename, moduleType) {
return `/**
* @license React
* ${filename}
*
${license}
*/
module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
`;
},
/***************** NODE_PROFILING (reconciler only) *****************/
[NODE_PROFILING](source, globalName, filename, moduleType) {
return `/**
* @license React
* ${filename}
*
${license}
*/
module.exports = function $$$reconciler($$$config) {
var exports = {};
${source}
return exports;
};
module.exports.default = module.exports;
Object.defineProperty(module.exports, "__esModule", { value: true });
`;
},
};
function wrapBundle(
function wrapWithTopLevelDefinitions(
source,
bundleType,
globalName,
@@ -457,17 +536,42 @@ function wrapBundle(
`Unsupported build type for the reconciler package: ${bundleType}.`
);
}
return wrapper(source, globalName, filename, moduleType);
}
// All the other packages.
const wrapper = wrappers[bundleType];
const wrapper = topLevelDefinitionWrappers[bundleType];
if (typeof wrapper !== 'function') {
throw new Error(`Unsupported build type: ${bundleType}.`);
}
return wrapper(source, globalName, filename, moduleType);
}
function wrapWithLicenseHeader(
source,
bundleType,
globalName,
filename,
moduleType
) {
if (bundleType === BROWSER_SCRIPT) {
// Bundles of type BROWSER_SCRIPT get sent straight to the browser without
// additional processing. So we should exclude any extra wrapper comments.
return source;
}
// All the other packages.
const wrapper = licenseHeaderWrappers[bundleType];
if (typeof wrapper !== 'function') {
throw new Error(`Unsupported build type: ${bundleType}.`);
}
return wrapper(source, globalName, filename, moduleType);
}
module.exports = {
wrapBundle,
wrapWithTopLevelDefinitions,
wrapWithLicenseHeader,
};