UNPKG

@warp-drive/build-config

Version:

Provides Build Configuration for projects using WarpDrive or EmberData

284 lines (270 loc) 10.3 kB
import EmbroiderMacros from '@embroider/macros/src/node.js'; import semver from 'semver'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { C as CURRENT_FEATURES } from './canary-features-CuKgaVtX.js'; import { L as LOGGING } from './debugging-VdsvkNjX.js'; function getEnv(forceMode) { const FORCE_TESTING = forceMode === 'testing' || forceMode === 'development' || forceMode === 'debug'; const FORCE_DEBUG = forceMode === 'development' || forceMode === 'debug'; const FORCE_PRODUCTION = forceMode === 'production'; const { EMBER_ENV, IS_TESTING, EMBER_CLI_TEST_COMMAND, NODE_ENV, CI, IS_RECORDING } = process.env; const PRODUCTION = FORCE_PRODUCTION || EMBER_ENV === 'production' || !EMBER_ENV && NODE_ENV === 'production'; const DEBUG = FORCE_DEBUG || !PRODUCTION; const TESTING = FORCE_TESTING || DEBUG || Boolean(EMBER_ENV === 'test' || IS_TESTING || EMBER_CLI_TEST_COMMAND); const SHOULD_RECORD = Boolean(!CI || IS_RECORDING); return { TESTING, PRODUCTION, DEBUG, IS_RECORDING: Boolean(IS_RECORDING), IS_CI: Boolean(CI), SHOULD_RECORD }; } // ======================== // FOR CONTRIBUTING AUTHORS // // Deprecations here should also have guides PR'd to the emberjs deprecation app // // github: https://github.com/ember-learn/deprecation-app // website: https://deprecations.emberjs.com // // Each deprecation should also be given an associated URL pointing to the // relevant guide. // // URLs should be of the form: https://deprecations.emberjs.com/v<major>.x#toc_<fileName> // where <major> is the major version of the deprecation and <fileName> is the // name of the markdown file in the guides repo. // // ======================== // const DEPRECATE_CATCH_ALL = '99.0'; const DEPRECATE_NON_STRICT_TYPES = '5.3'; const DEPRECATE_NON_STRICT_ID = '5.3'; const DEPRECATE_COMPUTED_CHAINS = '7.0'; const DEPRECATE_LEGACY_IMPORTS = '5.3'; const DEPRECATE_NON_UNIQUE_PAYLOADS = '5.3'; const DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE = '5.3'; const DEPRECATE_MANY_ARRAY_DUPLICATES = '5.3'; const DEPRECATE_STORE_EXTENDS_EMBER_OBJECT = '5.4'; const ENABLE_LEGACY_SCHEMA_SERVICE = '5.4'; const DEPRECATE_EMBER_INFLECTOR = '5.3'; const DISABLE_7X_DEPRECATIONS = '7.0'; const DEPRECATE_TRACKING_PACKAGE = '5.5'; const CURRENT_DEPRECATIONS = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ __proto__: null, DEPRECATE_CATCH_ALL, DEPRECATE_COMPUTED_CHAINS, DEPRECATE_EMBER_INFLECTOR, DEPRECATE_LEGACY_IMPORTS, DEPRECATE_MANY_ARRAY_DUPLICATES, DEPRECATE_NON_STRICT_ID, DEPRECATE_NON_STRICT_TYPES, DEPRECATE_NON_UNIQUE_PAYLOADS, DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE, DEPRECATE_STORE_EXTENDS_EMBER_OBJECT, DEPRECATE_TRACKING_PACKAGE, DISABLE_7X_DEPRECATIONS, ENABLE_LEGACY_SCHEMA_SERVICE }, Symbol.toStringTag, { value: 'Module' })); function deprecationIsResolved(deprecatedSince, compatVersion) { return semver.lte(semver.minVersion(deprecatedSince), semver.minVersion(compatVersion)); } const NextMajorVersion = '6.'; function deprecationIsNextMajorCycle(deprecatedSince) { return deprecatedSince.startsWith(NextMajorVersion); } function getDeprecations(compatVersion, deprecations) { const flags = {}; const keys = Object.keys(CURRENT_DEPRECATIONS); const DISABLE_7X_DEPRECATIONS = deprecations?.DISABLE_7X_DEPRECATIONS ?? true; keys.forEach(flag => { const deprecatedSince = CURRENT_DEPRECATIONS[flag]; const isDeactivatedDeprecationNotice = DISABLE_7X_DEPRECATIONS && deprecationIsNextMajorCycle(deprecatedSince); let flagState = true; // default to no code-stripping if (!isDeactivatedDeprecationNotice) { // if we have a specific flag setting, use it if (typeof deprecations?.[flag] === 'boolean') { flagState = deprecations?.[flag]; } else if (compatVersion) { // if we are told we are compatible with a version // we check if we can strip this flag const isResolved = deprecationIsResolved(deprecatedSince, compatVersion); // if we've resolved, we strip (by setting the flag to false) /* if (DEPRECATED_FEATURE) { // deprecated code path } else { // if needed a non-deprecated code path } */ flagState = !isResolved; } } // console.log(`${flag}=${flagState} (${deprecatedSince} <= ${compatVersion})`); flags[flag] = flagState; }); return flags; } const dirname = typeof __dirname !== 'undefined' ? __dirname : fileURLToPath(new URL(".", import.meta.url)); const relativePkgPath = path.join(dirname, '../package.json'); const version = JSON.parse(fs.readFileSync(relativePkgPath, 'utf-8')).version; const isCanary = version.includes('alpha'); function getFeatures(isProd) { const features = Object.assign({}, CURRENT_FEATURES); const keys = Object.keys(features); if (!isCanary) { // disable all features with a current value of `null` for (const feature of keys) { let featureValue = features[feature]; if (featureValue === null) { features[feature] = false; } } return features; } const FEATURE_OVERRIDES = process.env.WARP_DRIVE_FEATURE_OVERRIDE; if (FEATURE_OVERRIDES === 'ENABLE_ALL_OPTIONAL') { // enable all features with a current value of `null` for (const feature of keys) { let featureValue = features[feature]; if (featureValue === null) { features[feature] = true; } } } else if (FEATURE_OVERRIDES === 'DISABLE_ALL') { // disable all features, including those with a value of `true` for (const feature of keys) { features[feature] = false; } } else if (FEATURE_OVERRIDES) { // enable only the specific features listed in the environment // variable (comma separated) const forcedFeatures = FEATURE_OVERRIDES.split(','); for (let i = 0; i < forcedFeatures.length; i++) { let featureName = forcedFeatures[i]; if (!keys.includes(featureName)) { throw new Error(`Unknown feature flag: ${featureName}`); } features[featureName] = true; } } if (isProd) { // disable all features with a current value of `null` for (const feature of keys) { let featureValue = features[feature]; if (featureValue === null) { features[feature] = false; } } } return features; } function createLoggingConfig(env, debug) { const config = {}; const keys = Object.keys(LOGGING); for (const key of keys) { if (env.DEBUG || env.TESTING) { config[key] = true; } else { config[key] = debug[key] || false; } } return config; } /** * This package provides a build-plugin that enables configuration of deprecations, * optional features, development/testing support and debug logging. * * This configuration is done using `setConfig` in `ember-cli-build`. * * ```ts [ember-cli-build.js] * 'use strict'; * * const EmberApp = require('ember-cli/lib/broccoli/ember-app'); * * module.exports = async function (defaults) { * const { setConfig } = await import('@warp-drive/build-config'); // [!code focus] * * const app = new EmberApp(defaults, {}); * * setConfig(app, __dirname, { // [!code focus:3] * // settings here * }); * * const { Webpack } = require('@embroider/webpack'); * return require('@embroider/compat').compatBuild(app, Webpack, {}); * }; * * ``` * * Available settings include: * * - {@link LOGGING | debugging} * - {@link DEPRECATIONS | deprecations} * - {@link FEATURES | features} * - {@link WarpDriveConfig.polyfillUUID | polyfillUUID} * - {@link WarpDriveConfig.includeDataAdapterInProduction | includeDataAdapterInProduction} * - {@link WarpDriveConfig.compatWith | compatWith} * * * * @module */ const _MacrosConfig = EmbroiderMacros.MacrosConfig; function recastMacrosConfig(macros) { if (!('globalConfig' in macros)) { throw new Error('Expected MacrosConfig to have a globalConfig property'); } return macros; } function setConfig(context, appRootOrConfig, config) { const isEmberClassicUsage = arguments.length === 3; const macros = recastMacrosConfig(isEmberClassicUsage ? _MacrosConfig.for(context, appRootOrConfig) : context); const userConfig = isEmberClassicUsage ? config : appRootOrConfig; const isLegacySupport = userConfig.___legacy_support; const hasDeprecatedConfig = isLegacySupport && Object.keys(userConfig).length > 1; const hasInitiatedConfig = macros.globalConfig['WarpDrive']; // setConfig called by user prior to legacy support called if (isLegacySupport && hasInitiatedConfig) { if (hasDeprecatedConfig) { throw new Error('You have provided a config object to setConfig, but are also using the legacy emberData options key in ember-cli-build. Please remove the emberData key from options.'); } return; } // included hooks run during class initialization of the EmberApp instance // so our hook will run before the user has a chance to call setConfig // else we could print a useful message here // else if (isLegacySupport) { // console.warn( // `WarpDrive requires your ember-cli-build file to set a base configuration for the project.\n\nUsage:\n\t\`import { setConfig } from '@warp-drive/build-config';\n\tsetConfig(app, __dirname, {});\`` // ); // } const debugOptions = Object.assign({}, LOGGING, userConfig.debug); const env = getEnv(userConfig.forceMode); const DEPRECATIONS = getDeprecations(userConfig.compatWith || null, userConfig.deprecations); const FEATURES = getFeatures(env.PRODUCTION); const includeDataAdapterInProduction = typeof userConfig.includeDataAdapterInProduction === 'boolean' ? userConfig.includeDataAdapterInProduction : true; const includeDataAdapter = env.PRODUCTION ? includeDataAdapterInProduction : true; const finalizedConfig = { debug: debugOptions, polyfillUUID: userConfig.polyfillUUID ?? false, includeDataAdapter, compatWith: userConfig.compatWith ?? null, deprecations: DEPRECATIONS, features: FEATURES, activeLogging: createLoggingConfig(env, debugOptions), env }; macros.setGlobalConfig(import.meta.filename, 'WarpDrive', finalizedConfig); } export { setConfig };