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

Replace unreliable activation hook with default value for enabled modules #222

Merged
merged 5 commits into from
Mar 14, 2022
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
24 changes: 0 additions & 24 deletions admin/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -439,27 +439,3 @@ function perflab_plugin_action_links_add_settings( $links ) {

return $links;
}

/**
* Enables all non-experimental modules on plugin activation.
*
* @since 1.0.0
*/
function perflab_activation_hook() {
// Bail if option is already set with any value.
if ( false !== get_option( PERFLAB_MODULES_SETTING, false ) ) {
return;
}

$modules = perflab_get_modules();
$modules_settings = perflab_get_module_settings();

foreach ( $modules as $module_name => $module_data ) {
if ( ! $module_data['experimental'] ) {
$modules_settings[ $module_name ] = array( 'enabled' => true );
}
}

update_option( PERFLAB_MODULES_SETTING, $modules_settings );
}
register_activation_hook( PERFLAB_MAIN_FILE, 'perflab_activation_hook' );
12 changes: 12 additions & 0 deletions bin/plugin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const {
handler: translationsHandler,
options: translationsOptions,
} = require( './commands/translations' );
const {
handler: enabledModulesHandler,
options: enabledModulesOptions,
} = require( './commands/enabled-modules' );
const {
handler: sinceHandler,
options: sinceOptions,
Expand Down Expand Up @@ -65,4 +69,12 @@ withOptions( program.command( 'module-translations' ), translationsOptions )
)
.action( catchException( translationsHandler ) );

withOptions(
program.command( 'default-enabled-modules' ),
enabledModulesOptions
)
.alias( 'enabled-modules' )
.description( 'Generates a PHP file with non-experimental module slugs' )
.action( catchException( enabledModulesHandler ) );

program.parse( process.argv );
31 changes: 24 additions & 7 deletions bin/plugin/commands/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,43 @@ const glob = require( 'fast-glob' );
const fs = require( 'fs' );

/**
* @typedef WPModuleDescription
* @typedef WPModuleData
*
* @property {string} name Module name.
* @property {string} description Module description.
* @property {string} slug Module slug.
* @property {string} focus Module focus.
* @property {string} name Module name.
* @property {string} description Module description.
* @property {boolean} experimental Whether the module is experimental.
*/

/**
* Returns a promise resolving to the module description list string for the `readme.txt` file.
* Returns a promise resolving to the list of data for all modules.
*
* @param {string} modulesDir Modules directory.
*
* @return {Promise<[]WPModuleDescription>} Promise resolving to module description list.
* @return {Promise<[]WPModuleData>} Promise resolving to module data list.
*/
exports.getModuleDescriptions = async ( modulesDir ) => {
exports.getModuleData = async ( modulesDir ) => {
const moduleFilePattern = path.join( modulesDir, '*/*/load.php' );
const moduleFiles = await glob( path.resolve( '.', moduleFilePattern ) );

return moduleFiles
.map( ( moduleFile ) => {
// Populate slug and focus based on file path.
const moduleDir = path.dirname( moduleFile );
const moduleData = {
slug: path.basename( moduleDir ),
focus: path.basename( path.dirname( moduleDir ) ),
};

// Map of module header => object property.
const headers = {
'Module Name': 'name',
Description: 'description',
Experimental: 'experimental',
};
const moduleData = {};

// Populate name, description and experimental based on module file headers.
const fileContent = fs.readFileSync( moduleFile, 'utf8' );
const regex = new RegExp(
`^(?:[ \t]*<?php)?[ \t/*#@]*(${ Object.keys( headers ).join(
Expand All @@ -49,6 +60,12 @@ exports.getModuleDescriptions = async ( modulesDir ) => {
match = regex.exec( fileContent );
}

// Parse experimental field into a boolean.
if ( typeof moduleData.experimental === 'string' ) {
moduleData.experimental =
moduleData.experimental.toLowerCase() === 'yes';
}

return moduleData;
} )
.filter( ( moduleData ) => moduleData.name && moduleData.description );
Expand Down
121 changes: 121 additions & 0 deletions bin/plugin/commands/enabled-modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* External dependencies
*/
const path = require( 'path' );
const fs = require( 'fs' );
const { EOL } = require( 'os' );

/**
* Internal dependencies
*/
const { log, formats } = require( '../lib/logger' );
const { getModuleData } = require( './common' );

const TAB = '\t';
const NEWLINE = EOL;
const FILE_HEADER = `<?php
/* THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. */
return array(
`;
const FILE_FOOTER = `
);
/* THIS IS THE END OF THE GENERATED FILE */
`;

/**
* @typedef WPEnabledModulesCommandOptions
*
* @property {string=} directory Optional directory, default is the root `/modules` directory.
* @property {string=} output Optional output PHP file, default is the root `/default-enabled-modules.php` file.
*/

/**
* @typedef WPEnabledModulesSettings
*
* @property {string} directory Modules directory.
* @property {string} output Output PHP file.
*/

exports.options = [
{
argname: '-d, --directory <directory>',
description: 'Modules directory',
},
{
argname: '-d, --output <output>',
description: 'Output file',
},
];

/**
* Command that generates a PHP file with non-experimental module slugs.
*
* @param {WPEnabledModulesCommandOptions} opt
*/
exports.handler = async ( opt ) => {
await createEnabledModules( {
directory: opt.directory || 'modules',
output: opt.output || 'default-enabled-modules.php',
} );
};

/**
* Gathers the non-experimental modules as the default enabled modules.
*
* @param {WPEnabledModulesSettings} settings Default enabled modules settings.
*
* @return {[]string} List of default enabled module paths relative to modules directory.
*/
async function getDefaultEnabledModules( settings ) {
const modulesData = await getModuleData( settings.directory );
return modulesData
.filter( ( moduleData ) => ! moduleData.experimental )
.map( ( moduleData ) => `${ moduleData.focus }/${ moduleData.slug }` );
}

/**
* Creates PHP file with the given default enabled modules.
*
* @param {[]string} enabledModules List of default enabled module paths relative to modules directory.
* @param {WPEnabledModulesSettings} settings Default enabled modules settings.
*/
function createEnabledModulesPHPFile( enabledModules, settings ) {
const output = enabledModules.map( ( enabledModule ) => {
// Escape single quotes.
return `${ TAB }'${ enabledModule.replace( /'/g, "\\'" ) }',`;
} );

const fileOutput = `${ FILE_HEADER }${ output.join(
NEWLINE
) }${ FILE_FOOTER }`;
fs.writeFileSync( path.join( '.', settings.output ), fileOutput );
}

/**
* Gathers non-experimental modules and generates a PHP file with them.
*
* @param {WPEnabledModulesSettings} settings Default enabled modules settings.
*/
async function createEnabledModules( settings ) {
log(
formats.title(
`\n💃Gathering non-experimental modules for "${ settings.directory }" in "${ settings.output }"\n\n`
)
);

try {
const enabledModules = await getDefaultEnabledModules( settings );
createEnabledModulesPHPFile( enabledModules, settings );
} catch ( error ) {
if ( error instanceof Error ) {
log( formats.error( error.stack ) );
return;
}
}

log(
formats.success(
`\n💃Non-experimental modules successfully set in "${ settings.output }"\n\n`
)
);
}
8 changes: 3 additions & 5 deletions bin/plugin/commands/readme.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const fs = require( 'fs' );
*/
const { log, formats } = require( '../lib/logger' );
const config = require( '../config' );
const { getModuleDescriptions } = require( './common' );
const { getModuleData } = require( './common' );
const { getChangelog } = require( './changelog' );

/**
Expand Down Expand Up @@ -68,11 +68,9 @@ exports.handler = async ( opt ) => {
* @return {Promise<string>} Promise resolving to module description list in markdown, with trailing newline.
*/
async function getModuleDescriptionList( settings ) {
const moduleDescriptions = await getModuleDescriptions(
settings.directory
);
const modulesData = await getModuleData( settings.directory );

return moduleDescriptions
return modulesData
.map(
( moduleData ) =>
`* **${ moduleData.name }:** ${ moduleData.description }`
Expand Down
8 changes: 3 additions & 5 deletions bin/plugin/commands/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { EOL } = require( 'os' );
*/
const { log, formats } = require( '../lib/logger' );
const config = require( '../config' );
const { getModuleDescriptions } = require( './common' );
const { getModuleData } = require( './common' );

const TAB = '\t';
const NEWLINE = EOL;
Expand Down Expand Up @@ -77,10 +77,8 @@ exports.handler = async ( opt ) => {
* @return {[]WPTranslationEntry} List of translation entries.
*/
async function getTranslations( settings ) {
const moduleDescriptions = await getModuleDescriptions(
settings.directory
);
const moduleTranslations = moduleDescriptions.map( ( moduleData ) => {
const modulesData = await getModuleData( settings.directory );
const moduleTranslations = modulesData.map( ( moduleData ) => {
return [
{
text: moduleData.name,
Expand Down
8 changes: 8 additions & 0 deletions default-enabled-modules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
/* THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. */
return array(
'images/webp-uploads',
'object-cache/persistent-object-cache-health-check',
'site-health/webp-support',
);
/* THIS IS THE END OF THE GENERATED FILE */
4 changes: 4 additions & 0 deletions docs/Releasing-the-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ The version number needs to be updated in the following files:

In addition to those locations, run the `npm run since -- -r {version}` command to replace any occurrence of `@since n.e.x.t` with the version number. This ensures any code annotated with the "next" release will now have its proper version number on it. The only exception to this are pre-releases, such as a beta or RC: For those, the stable version number should be used. For example, if the milestone is `1.2.0-beta.2`, the version in e.g. `@since` annotations in the codebase should still be `1.2.0`.

### Update list of default enabled modules

The default enabled modules are defined in a separate `default-enabled-modules.php` file which can be automatically generated and updated. Run `npm run enabled-modules` to update the file based on the latest available non-experimental modules.

### Update translation strings

The module headers from the plugin have to be translated using a separate `module-i18n.php` file which can be automatically generated and updated. Run `npm run translations` to update the file to reflect the latest available modules.
Expand Down
15 changes: 13 additions & 2 deletions load.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@
* @since 1.0.0
*/
function perflab_register_modules_setting() {
// To set the default value for which modules are enabled, rely on this generated file.
$default_enabled_modules = require plugin_dir_path( __FILE__ ) . 'default-enabled-modules.php';
$default_option = array_reduce(
$default_enabled_modules,
function( $module_settings, $module_dir ) {
$module_settings[ $module_dir ] = array( 'enabled' => true );
return $module_settings;
},
array()
);

register_setting(
PERFLAB_MODULES_SCREEN,
PERFLAB_MODULES_SETTING,
array(
'type' => 'object',
'sanitize_callback' => 'perflab_sanitize_modules_setting',
'default' => array(),
'default' => $default_option,
)
);
}
Expand Down Expand Up @@ -76,7 +87,7 @@ function( $module_settings ) {
* @return array Associative array of module settings keyed by module slug.
*/
function perflab_get_module_settings() {
return (array) get_option( PERFLAB_MODULES_SETTING, array() );
return (array) get_option( PERFLAB_MODULES_SETTING );
mitogh marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"since": "./bin/plugin/cli.js since",
"readme": "./bin/plugin/cli.js readme",
"translations": "./bin/plugin/cli.js translations",
"enabled-modules": "./bin/plugin/cli.js enabled-modules",
"format-js": "wp-scripts format ./bin",
"lint-js": "wp-scripts lint-js ./bin",
"format-php": "wp-env run composer run-script format",
Expand Down
Loading