diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..b7310e5eb --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": ["@wordpress/babel-preset-default"], + "plugins": [ + [ + "@wordpress/babel-plugin-makepot", + { + "output": "languages/learnpress-js.pot" + } + ] + ] +} diff --git a/Gulpfile.js b/Gulpfile.js index d32c05ef0..f221d9638 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -68,6 +68,8 @@ const releasesFiles = [ '!editorconfig', '!.travis.yml', '!.babelrc', + '!inc/**/*.http', + '!packages/**', ]; const errorHandler = ( r ) => { @@ -86,7 +88,7 @@ gulp.task( 'styles', () => { .src( [ 'assets/src/scss/**/*.scss' ] ) .pipe( plumber( errorHandler ) ) // .pipe( sourcemaps.init() ) - .pipe(sass.sync().on('error', sass.logError)) + .pipe( sass.sync().on( 'error', sass.logError ) ) // .pipe( sourcemaps.write( './' ) ) .pipe( lineec() ) .pipe( gulp.dest( 'assets/css' ) ) @@ -222,3 +224,11 @@ gulp.task( 'release', gulp.series( 'build', 'noticeReleases', ( done ) => { gulp.task( 'release1', gulp.series( 'build', 'cleanReleaseFolder', 'noticeReleases', ( done ) => { done(); } ) ); + +gulp.task( 'updatePot', () => { + return gulp + .src( [ './languages/learnpress.pot' ] ) + .pipe( replace( /(assets\/)(.*)(.js)/g, 'assets/js/dist/frontend/quiz.min.js' ) ) + .pipe( gulp.dest( './languages/' ) ); +} ); + diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index b541a5f31..000000000 --- a/babel.config.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = function (api) { - api.cache(true); - const presets = [ - ["@babel/preset-env", {}], - ["@babel/preset-react", {}, 'a'], - ["@babel/env", {}, 'b'], - ["@babel/react", {}, 'c'] - ]; - - // const presets = [ - // "es2015", - // "env", - // "react", - // "stage-0" - // ]; - - - const plugins = [ - ["@babel/plugin-proposal-class-properties", {}] - ]; - - return { - presets, - plugins - }; -} \ No newline at end of file diff --git a/inc/admin/class-lp-admin-assets.php b/inc/admin/class-lp-admin-assets.php index 3da59b2d4..e32564a35 100644 --- a/inc/admin/class-lp-admin-assets.php +++ b/inc/admin/class-lp-admin-assets.php @@ -73,7 +73,7 @@ protected function _get_scripts(): array { 'learn-press/admin-default-scripts', array( // need build if change source vue - 'vue-libs' => new LP_Asset_Key( $this->url( 'js/vendor/vue/vue_libs' . self::$_min_assets . '.js' ) ), + 'vue-libs' => new LP_Asset_Key( $this->url( 'js/vendor/vue/vue_libs.js' ) ), 'select2' => new LP_Asset_Key( $this->url( 'src/js/vendor/select2.full.min.js' ) ), 'jquery-tipsy' => new LP_Asset_Key( $this->url( 'src/js/vendor/jquery/jquery-tipsy.js' ) ), 'jspdf' => new LP_Asset_Key( $this->url( 'src/js/vendor/jspdf.min.js' ) ), diff --git a/package.json b/package.json index 22e0532fd..69a1291c9 100644 --- a/package.json +++ b/package.json @@ -1,93 +1,70 @@ { - "name": "learnpress", - "version": "1.0.0", - "description": "[![Stories in Ready](https://badge.waffle.io/LearnPress/LearnPress.svg?label=ready&title=Ready)](http://waffle.io/LearnPress/LearnPress)", - "main": "index.js", - "scripts": { - "test": "./vendor/bin/phpcs -d date.timezone=UTC", - "dev": "cross-env BABEL_ENV=default webpack --mode=development", - "dev-watch": "cross-env BABEL_ENV=default webpack --mode=development --watch", - "dev-packages": "cross-env BABEL_ENV=default webpack --config tools/build.packages.js --mode=development --watch", - "build-packages": "cross-env BABEL_ENV=default NODE_ENV=production webpack --config tools/build.packages.js --progress", - "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack --progress", - "prod": "webpack --progress --colors -p --config webpack.config.js --mode=production", - "dev-build": "npm run build && npm run build-packages && gulp styles && npm run dev && npm run dev-packages", - "release": "npm run build && npm run build-packages && gulp release", - "makepot": "wp i18n make-pot . --skip-audit --exclude=\"test,releases,build,tools,dist,node_modules,vendor,wordpress,\" --headers='{\"last-translator\":\"admin@email.com\",\"Report-Msgid-Bugs-To\":\"https://github.com/LearnPress/learnpress/issues\"}' languages/learnpress.pot" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/LearnPress/LearnPress.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/LearnPress/LearnPress/issues" - }, - "homepage": "https://github.com/LearnPress/LearnPress#readme", - "devDependencies": { - "@babel/plugin-proposal-class-properties": "^7.7.0", - "@babel/preset-env": "^7.16.5", - "@babel/preset-react": "^7.0.0", - "@wordpress/babel-preset-default": "^1.2.0", - "@wordpress/eslint-plugin": "^9.3.0", - "@wordpress/stylelint-config": "19.1.0", - "babel-loader": "^8.2.3", - "beepbeep": "^1.3.0", - "classnames": "^2.2.5", - "cross-env": "^5.1.5", - "css-loader": "^0.28.11", - "del": "^6.0.0", - "eslint": "^7.28.0", - "fs": "0.0.1-security", - "gulp": "^4.0.2", - "gulp-cache": "^1.1.3", - "gulp-line-ending-corrector": "^1.0.3", - "gulp-notify": "^3.2.0", - "gulp-plumber": "^1.2.1", - "gulp-postcss": "^9.0.0", - "gulp-rename": "^2.0.0", - "gulp-replace": "^0.5.4", - "gulp-rtlcss": "^1.4.2", - "gulp-sass": "^5.0.0", - "gulp-sourcemaps": "^2.6.5", - "gulp-uglify": "^3.0.2", - "gulp-uglify-es": "^2.0.0", - "gulp-uglifycss": "^1.1.0", - "gulp-vinyl-zip": "^2.2.1", - "gulp-wp-pot": "^2.5.0", - "jquery": "^3.4.1", - "mkdir": "0.0.2", - "mkdirp": "^0.5.1", - "postcss-loader": "^2.1.5", - "prettier-stylelint": "^0.4.2", - "read-file": "^0.2.0", - "sass": "^1.43.4", - "stylelint": "^13.7.0", - "stylelint-order": "^4.1.0", - "uglify-js": "^3.13.9", - "uglifycss": "0.0.29", - "webpack-merge-and-include-globally": "^2.3.3", - "yargs": "^7.1.0" - }, - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.7.0", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@learnpress/custom-templated-path-webpack-plugin": "^1.0.0", - "@learnpress/dependency-extraction-webpack-plugin": "^1.0.0", - "@wordpress/block-editor": "^8.1.0", - "@wordpress/blocks": "^11.2.0", - "@wordpress/components": "^19.3.0", - "@wordpress/custom-templated-path-webpack-plugin": "^1.5.0", - "@wordpress/dependency-extraction-webpack-plugin": "^2.0.0", - "@wordpress/i18n": "^3.15.0", - "@wordpress/icons": "^6.2.0", - "@wordpress/scripts": "^20.0.2", - "copy-webpack-plugin": "^5.0.4", - "gulp-cli": "^2.3.0", - "react-easy-crop": "^4.0.1", - "refx": "^3.1.1", - "sortablejs": "^1.13.0" - } + "name": "learnpress", + "version": "4.0.0", + "description": "[![Stories in Ready](https://badge.waffle.io/LearnPress/LearnPress.svg?label=ready&title=Ready)](http://waffle.io/LearnPress/LearnPress)", + "main": "index.js", + "scripts": { + "dev": "wp-scripts start", + "build": "wp-scripts build", + "dev-build": "npm run build && gulp styles && npm run dev", + "release": "npm run build && npm run makepot && gulp release", + "babel:makepot": "babel assets/src", + "makepot:cli": "wp i18n make-pot . languages/learnpress.pot --skip-audit --merge=languages/learnpress-js.pot --exclude=\"test,releases,build,tools,dist,node_modules,vendor,wordpress,\" --headers='{\"last-translator\":\"admin@email.com\",\"Report-Msgid-Bugs-To\":\"https://github.com/LearnPress/learnpress/issues\"}'", + "makepot": "npm run babel:makepot && makepot:cli && gulp updatePot" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/LearnPress/LearnPress.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/LearnPress/LearnPress/issues" + }, + "homepage": "https://github.com/LearnPress/LearnPress#readme", + "devDependencies": { + "@babel/cli": "^7.17.6", + "@wordpress/babel-plugin-makepot": "^3.4.0", + "@wordpress/babel-preset-default": "^6.6.1", + "@wordpress/scripts": "^22.2.1", + "beepbeep": "^1.3.0", + "classnames": "^2.2.5", + "del": "^6.0.0", + "gulp": "^4.0.2", + "gulp-cache": "^1.1.3", + "gulp-line-ending-corrector": "^1.0.3", + "gulp-notify": "^3.2.0", + "gulp-plumber": "^1.2.1", + "gulp-postcss": "^9.0.0", + "gulp-rename": "^2.0.0", + "gulp-replace": "^0.5.4", + "gulp-rtlcss": "^1.4.2", + "gulp-sass": "^5.0.0", + "gulp-sourcemaps": "^2.6.5", + "gulp-uglify": "^3.0.2", + "gulp-uglify-es": "^2.0.0", + "gulp-uglifycss": "^1.1.0", + "gulp-vinyl-zip": "^2.2.1", + "gulp-wp-pot": "^2.5.0", + "jquery": "^3.4.1", + "read-file": "^0.2.0", + "sass": "^1.43.4", + "stylelint": "^13.7.0", + "stylelint-order": "^4.1.0", + "uglify-js": "^3.13.9", + "uglifycss": "0.0.29", + "webpack": "^5.70.0", + "webpack-merge-and-include-globally": "^2.3.4" + }, + "dependencies": { + "@wordpress/block-editor": "^8.1.0", + "@wordpress/blocks": "^11.2.0", + "@wordpress/components": "^19.3.0", + "@wordpress/icons": "^6.2.0", + "copy-webpack-plugin": "^5.0.4", + "gulp-cli": "^2.3.0", + "react-easy-crop": "^4.0.1", + "refx": "^3.1.1", + "sortablejs": "^1.13.0" + } } diff --git a/packages/dependecy-extraction-webpack-plugin/index.js b/packages/dependecy-extraction-webpack-plugin/index.js new file mode 100644 index 000000000..047c21381 --- /dev/null +++ b/packages/dependecy-extraction-webpack-plugin/index.js @@ -0,0 +1,292 @@ +/** + * External dependencies + */ + const { createHash } = require( 'crypto' ); + const path = require( 'path' ); + const webpack = require( 'webpack' ); + // In webpack 5 there is a `webpack.sources` field but for webpack 4 we have to fallback to the `webpack-sources` package. + const { RawSource } = webpack.sources || require( 'webpack-sources' ); + const json2php = require( 'json2php' ); + const isWebpack4 = webpack.version.startsWith( '4.' ); + + /** + * Internal dependencies + */ + const { + defaultRequestToExternal, + defaultRequestToHandle, + } = require( './util' ); + + const defaultExternalizedReportFileName = 'externalized-dependencies.json'; + + class DependencyExtractionWebpackPlugin { + constructor( options ) { + this.options = Object.assign( + { + combineAssets: false, + combinedOutputFile: null, + externalizedReport: false, + injectPolyfill: false, + outputFormat: 'php', + outputFilename: null, + useDefaults: true, + }, + options + ); + + /* + * Track requests that are externalized. + * + * Because we don't have a closed set of dependencies, we need to track what has + * been externalized so we can recognize them in a later phase when the dependency + * lists are generated. + */ + this.externalizedDeps = new Set(); + + // Offload externalization work to the ExternalsPlugin. + this.externalsPlugin = new webpack.ExternalsPlugin( + 'window', + isWebpack4 + ? this.externalizeWpDeps.bind( this ) + : this.externalizeWpDepsV5.bind( this ) + ); + } + + externalizeWpDeps( _context, request, callback ) { + let externalRequest; + + // Handle via options.requestToExternal first. + if ( typeof this.options.requestToExternal === 'function' ) { + externalRequest = this.options.requestToExternal( request ); + } + + // Cascade to default if unhandled and enabled. + if ( + typeof externalRequest === 'undefined' && + this.options.useDefaults + ) { + externalRequest = defaultRequestToExternal( request ); + } + + if ( externalRequest ) { + this.externalizedDeps.add( request ); + + return callback( null, externalRequest ); + } + + return callback(); + } + + externalizeWpDepsV5( { context, request }, callback ) { + return this.externalizeWpDeps( context, request, callback ); + } + + mapRequestToDependency( request ) { + // Handle via options.requestToHandle first. + if ( typeof this.options.requestToHandle === 'function' ) { + const scriptDependency = this.options.requestToHandle( request ); + if ( scriptDependency ) { + return scriptDependency; + } + } + + // Cascade to default if enabled. + if ( this.options.useDefaults ) { + const scriptDependency = defaultRequestToHandle( request ); + if ( scriptDependency ) { + return scriptDependency; + } + } + + // Fall back to the request name. + return request; + } + + stringify( asset ) { + if ( this.options.outputFormat === 'php' ) { + return ` + this.addAssets( compilation, compiler ) + ); + } else { + compiler.hooks.thisCompilation.tap( + this.constructor.name, + ( compilation ) => { + compilation.hooks.processAssets.tap( + { + name: this.constructor.name, + stage: + compiler.webpack.Compilation + .PROCESS_ASSETS_STAGE_ADDITIONAL, + }, + () => this.addAssets( compilation, compiler ) + ); + } + ); + } + } + + addAssets( compilation, compiler ) { + const { + combineAssets, + combinedOutputFile, + externalizedReport, + injectPolyfill, + outputFormat, + outputFilename, + } = this.options; + + // Dump actually externalized dependencies to a report file. + if ( externalizedReport ) { + const externalizedReportFile = + typeof externalizedReport === 'string' + ? externalizedReport + : defaultExternalizedReportFileName; + compilation.emitAsset( + externalizedReportFile, + new RawSource( + JSON.stringify( Array.from( this.externalizedDeps ).sort() ) + ) + ); + } + + const combinedAssetsData = {}; + + // Process each entry point independently. + for ( const [ + entrypointName, + entrypoint, + ] of compilation.entrypoints.entries() ) { + const entrypointExternalizedWpDeps = new Set(); + if ( injectPolyfill ) { + entrypointExternalizedWpDeps.add( 'wp-polyfill' ); + } + + const processModule = ( { userRequest } ) => { + if ( this.externalizedDeps.has( userRequest ) ) { + const scriptDependency = this.mapRequestToDependency( + userRequest + ); + entrypointExternalizedWpDeps.add( scriptDependency ); + } + }; + + // Search for externalized modules in all chunks. + for ( const chunk of entrypoint.chunks ) { + const modulesIterable = isWebpack4 + ? chunk.modulesIterable + : compilation.chunkGraph.getChunkModules( chunk ); + for ( const chunkModule of modulesIterable ) { + processModule( chunkModule ); + // Loop through submodules of ConcatenatedModule. + if ( chunkModule.modules ) { + for ( const concatModule of chunkModule.modules ) { + processModule( concatModule ); + } + } + } + } + + const entrypointChunk = isWebpack4 + ? entrypoint.chunks.find( ( c ) => c.name === entrypointName ) + : entrypoint.getEntrypointChunk(); + + const assetData = { + // Get a sorted array so we can produce a stable, stringified representation. + dependencies: Array.from( entrypointExternalizedWpDeps ).sort(), + version: entrypointChunk.hash, + }; + + const assetString = this.stringify( assetData ); + + // Determine a filename for the asset file. + const [ filename, query ] = entrypointName.split( '?', 2 ); + const buildFilename = compilation.getPath( + compiler.options.output.filename, + { + chunk: entrypointChunk, + filename, + query, + basename: basename( filename ), + contentHash: createHash( 'md4' ) + .update( assetString ) + .digest( 'hex' ), + } + ); + + if ( combineAssets ) { + combinedAssetsData[ buildFilename ] = assetData; + continue; + } + + let assetFilename; + + if ( outputFilename ) { + assetFilename = compilation.getPath( outputFilename, { + chunk: entrypointChunk, + filename, + query, + basename: basename( filename ), + contentHash: createHash( 'md4' ) + .update( assetString ) + .digest( 'hex' ), + } ); + } else { + assetFilename = buildFilename.replace( + /\.js$/i, + '.asset.' + ( outputFormat === 'php' ? 'php' : 'json' ) + ); + } + + // Add source and file into compilation for webpack to output. + compilation.assets[ assetFilename ] = new RawSource( assetString ); + entrypointChunk.files[ isWebpack4 ? 'push' : 'add' ]( + assetFilename + ); + } + + if ( combineAssets ) { + // Assert the `string` type for output path. + // The type indicates the option may be `undefined`. + // However, at this point in compilation, webpack has filled the options in if + // they were not provided. + const outputFolder = /** @type {{path:string}} */ ( compiler.options + .output ).path; + + const assetsFilePath = path.resolve( + outputFolder, + combinedOutputFile || + 'assets.' + ( outputFormat === 'php' ? 'php' : 'json' ) + ); + const assetsFilename = path.relative( + outputFolder, + assetsFilePath + ); + + // Add source into compilation for webpack to output. + compilation.assets[ assetsFilename ] = new RawSource( + this.stringify( combinedAssetsData ) + ); + } + } + } + + function basename( name ) { + if ( ! name.includes( '/' ) ) { + return name; + } + return name.substr( name.lastIndexOf( '/' ) + 1 ); + } + + module.exports = DependencyExtractionWebpackPlugin; diff --git a/packages/dependecy-extraction-webpack-plugin/package.json b/packages/dependecy-extraction-webpack-plugin/package.json new file mode 100644 index 000000000..5babc6de0 --- /dev/null +++ b/packages/dependecy-extraction-webpack-plugin/package.json @@ -0,0 +1,6 @@ +{ + "name": "@learnpress/dependency-extraction-webpack-plugin", + "private": true, + "license": "MIT", + "main": "index.js" + } diff --git a/packages/dependecy-extraction-webpack-plugin/util.js b/packages/dependecy-extraction-webpack-plugin/util.js new file mode 100644 index 000000000..e8aa6c119 --- /dev/null +++ b/packages/dependecy-extraction-webpack-plugin/util.js @@ -0,0 +1,112 @@ +let globalOptions = { + namespace: '@learnpress', + library: 'LP', +}; + +/** + * Default request to global transformation + * + * Transform @wordpress dependencies: + * + * @wordpress/api-fetch -> wp.apiFetch + * @wordpress/i18n -> wp.i18n + * + * @param {string} request Requested module + * @param {Object} options + * + * @return {(string|string[]|undefined)} Script global + */ +function defaultRequestToExternal( request, options ) { + globalOptions = Object.assign( + { + namespace: '@learnpress', + library: 'LP', + }, + options + ); + + switch ( request ) { + case 'moment': + return request; + + case '@babel/runtime/regenerator': + return 'regeneratorRuntime'; + + case 'lodash': + case 'lodash-es': + return 'lodash'; + + case 'jquery': + return 'jQuery'; + + case 'react': + return 'React'; + + case 'react-dom': + return 'ReactDOM'; + } + + const namespace = globalOptions.namespace + '/'; + + if ( request.startsWith( namespace ) ) { + return [ globalOptions.library, camelCaseDash( request.substring( namespace.length ) ) ]; + } +} + +/** + * Default request to WordPress script handle transformation + * + * Transform @wordpress dependencies: + * + * @wordpress/i18n -> wp-i18n + * @wordpress/escape-html -> wp-escape-html + * + * @param {string} request Requested module + * @param {Object} options + * + * @return {(string|undefined)} Script handle + */ +function defaultRequestToHandle( request, options ) { + globalOptions = Object.assign( + { + namespace: '@learnpress', + library: 'LP', + }, + options + ); + + switch ( request ) { + case '@babel/runtime/regenerator': + return 'wp-polyfill'; + + case 'lodash-es': + return 'lodash'; + } + + const namespace = globalOptions.namespace + '/'; + + if ( request.startsWith( namespace ) ) { + return ( globalOptions.library ) + '-' + request.substring( namespace.length ); + } +} + +/** + * Given a string, returns a new string with dash separators converted to + * camelCase equivalent. This is not as aggressive as `_.camelCase` in + * converting to uppercase, where Lodash will also capitalize letters + * following numbers. + * + * Temporarily duplicated from @wordpress/scripts/utils. + * + * @param {string} string Input dash-delimited string. + * + * @return {string} Camel-cased string. + */ +function camelCaseDash( string ) { + return string.replace( /-([a-z])/g, ( match, letter ) => letter.toUpperCase() ); +} + +module.exports = { + defaultRequestToExternal, + defaultRequestToHandle, +}; diff --git a/tools/build.packages.js b/tools/build.packages.js deleted file mode 100644 index 600cbd408..000000000 --- a/tools/build.packages.js +++ /dev/null @@ -1,190 +0,0 @@ -const path = require( 'path' ); -const webpack = require( 'webpack' ); -const CopyPlugin = require( 'copy-webpack-plugin' ); -const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' ); -const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); -const LearnPressCustomTemplatedPathPlugin = require( '@learnpress/custom-templated-path-webpack-plugin' ); -const LearnPressDependencyExtractionWebpackPlugin = require( '@learnpress/dependency-extraction-webpack-plugin' ); -const MergeIntoSingleFilePlugin = require( 'webpack-merge-and-include-globally' ); - -const { get, escapeRegExp, compact } = require( 'lodash' ); -const { basename, sep } = require( 'path' ); -const args = require( 'yargs' ).argv; - -const baseDir = path.join( __dirname, '../' ); -const buildPath = __dirname + '/build'; -const { camelCaseDash } = require( '@wordpress/dependency-extraction-webpack-plugin/lib/util' ); -const packageDir = baseDir + '/assets/src/apps'; -const buildPackages = [ - - // Global - 'data-controls', - - // Admin - //'admin/react/data-controls', - //'admin/react/question-editor', - - // Frontend - 'frontend/modal', - 'frontend/courses', - 'frontend/checkout', - 'frontend/single-course', - 'frontend/single-curriculum', - 'frontend/question-types', - 'frontend/lesson', - 'frontend/quiz', - 'frontend/lp-configs', - //'frontend/data-controls', - 'frontend/custom', - 'frontend/profile', - 'frontend/widgets', - 'blocks/index', -]; - -const { - NODE_ENV: mode = 'development', - WP_DEVTOOL: devtool = ( mode === 'production' ? false : 'source-map' ), -} = process.env; -const isDev = mode !== 'production'; -const isWatch = !! args.watch; - -module.exports = function( env = { environment: 'production', watch: false, buildTarget: false } ) { - const config = { - mode, - entry: buildPackages.reduce( ( memo, slug ) => { - const basename = path.basename( slug ); - let name = camelCaseDash( basename ); - if ( 'lpConfigs' === name ) { - name = 'config'; - } - memo[ name ] = path.resolve( packageDir, `js/${ slug }.js` ); - return memo; - }, {} ), - output: { - path: path.resolve( baseDir, 'assets/js/dist' ), - filename: '[LP_BASEPATH]/[LP_BASENAME]' + ( isDev ? '' : '.min' ) + '.js', - library: [ 'LP', '[name]' ], - libraryTarget: 'this', - }, - watch: isWatch, //'production' !== process.env.NODE_ENV, - module: { - rules: [ - { - test: /\.js$/, - exclude: /(node_modules|bower_components)/, - use: { - loader: 'babel-loader', - options: { - //presets: ['babel-preset-env'] - presets: [ '@babel/preset-env' ], - }, - }, - }, - ], - }, - plugins: [ - new CustomTemplatedPathPlugin( { - // basename(path, data) { - // let rawRequest; - // - // const entryModule = get(data, ['chunk', 'entryModule'], {}); - // switch (entryModule.type) { - // case 'javascript/auto': - // rawRequest = entryModule.rawRequest; - // break; - // - // case 'javascript/esm': - // rawRequest = entryModule.rootModule.rawRequest; - // break; - // } - // - // if (rawRequest) { - // return basename(rawRequest); - // } - // - // return path; - // } - } ), - new DependencyExtractionWebpackPlugin(), - new LearnPressCustomTemplatedPathPlugin( { - LP_BASENAME( p, data ) { - let rawRequest; - - const entryModule = get( data, [ 'chunk', 'entryModule' ], {} ); - switch ( entryModule.type ) { - case 'javascript/auto': - rawRequest = entryModule.rawRequest; - break; - - case 'javascript/esm': - rawRequest = entryModule.rootModule.rawRequest; - break; - } - - if ( rawRequest ) { - return path.basename( rawRequest ).replace( /\.js$/, '' ); - } - - return p; - }, - LP_BASEPATH( p, data ) { - let rawRequest; - - const entryModule = get( data, [ 'chunk', 'entryModule' ], {} ); - switch ( entryModule.type ) { - case 'javascript/auto': - rawRequest = entryModule.rawRequest; - break; - - case 'javascript/esm': - rawRequest = entryModule.rootModule.rawRequest; - break; - } - - if ( rawRequest ) { - return path.basename( path.dirname( rawRequest ) ); - } - - return p; - }, - } ), - - new LearnPressDependencyExtractionWebpackPlugin( { - namespace: '@learnpress', - library: 'LP', - } ), - - // new CopyPlugin(editorPackages.map((name) => { - // return { - // from: packageDir + `/packages/${name}/package.json`, - // to: path.resolve(__dirname, 'build') + `/${name}/` - // } - // }).concat(editorPackages.map((name) => { - // return { - // from: path.resolve(__dirname, 'build') + `/${name}/index.js.map`, - // to: packageDir + `/assets/js/${name}.js.map` - // } - // }))), - // - // new MergeIntoSingleFilePlugin({ - // files: editorPackages.reduce((memo, packageName) => { - // const name = `../src/assets/js/${ packageName }.js`; - // memo[name] = [__dirname + `/build/${packageName}/index.js`]; - // return memo; - // }, {}), - // transform: editorPackages.reduce((memo, packageName) => { - // const name = `../src/assets/js/${ packageName }.js`; - // memo[name] = function (code) { - // return code.replace(/index.js.map/, `${packageName}.js.map`); - // } - // return memo; - // }, {}) - // }) - //blocksCSSPlugin, - ], - devtool, - //...buildConfig(env) - }; - - return config; -}; diff --git a/tools/webpack.js b/tools/webpack.js deleted file mode 100644 index 56466286b..000000000 --- a/tools/webpack.js +++ /dev/null @@ -1,117 +0,0 @@ -const MergeIntoSingleFilePlugin = require( 'webpack-merge-and-include-globally' ); -const uglifyJS = require( 'uglify-js' ); -const uglifyCSS = require( 'uglifycss' ); - -const isCompressed = function isCompressed( code, n = 5 ) { - const m = code.match( /\n/gm ); - - return ! m || ( m.length <= n ); -}; - -const minifyJsDest = function minifyJsDest( dest, code, isDev ) { - if ( isDev === undefined ) { - isDev = process.env.NODE_ENV !== 'production'; - } - - let min = ! isDev ? '.min' : ''; - - if ( ! min && isCompressed( code ) ) { - min = '.min'; - } - - if ( dest.indexOf( '.min' ) !== -1 ) { - min = ''; - } - code = ! isDev ? uglifyJS.minify( code ).code : code; - - return { - [ `${ dest }${ min }.js` ]: code, - }; -}; - -const minifyCssDest = function minifyCssDest( dest, code ) { - const isDev = false;//process.env.NODE_ENV !== 'production'; - const min = ! isDev ? '.min' : ''; - code = ! isDev ? uglifyCSS.processString( code ) : code; - - return { - [ `${ dest }${ min }.css` ]: code, - }; -}; - -const isDev = function isDev() { - return process.env.NODE_ENV !== 'production'; -}; - -const adminSources = () => [ - //'./assets/src/js/vendor/vue/vue.js', - // './assets/src/js/vendor/vue/vuex.js', - // './assets/src/js/vendor/vue/vue-resource.js', - // './assets/src/js/vendor/vue/vue-draggable.js', - //'./assets/src/js/vendor/jquery/jquery.tipsy.js', - // './assets/src/js/vendor/chart.min.js', -]; - -const frontendSources = () => [ - // './assets/src/js/vendor/vue/vue' + ( isDev() ? '' : '.min' ) + '.js', - // './assets/src/js/vendor/vue/vuex.js', - // './assets/src/js/vendor/vue/vue-resource.js', - // './assets/src/js/vendor/vue_libs_c.min.js', - './assets/src/js/vendor/watch.js', - './assets/src/js/vendor/jquery/jquery-scrollTo.js', - './assets/src/js/vendor/jquery/jquery-timer.js', - './assets/src/js/vendor/jquery/jquery.tipsy.js', -]; - -/** Merge vue libs **/ -const vueSources = () => [ - './assets/src/js/vendor/vue/vue.js', - './assets/src/js/vendor/vue/vuex.js', - './assets/src/js/vendor/vue/vue-resource.js', -]; - -const options = { - files: [ - { // Run this can error code vue - add on frontend editor - src: frontendSources(), - dest( code ) { - return minifyJsDest( 'assets/js/vendor/plugins.all', code ); - }, - }, - // { - // src: adminSources(), - // dest: function (code) { - // return minifyJsDest('assets/js/vendor/admin.plugins.all', code); - // } - // }, - { - src: vueSources(), - dest( code ) { - return minifyJsDest( 'assets/js/vendor/vue/vue_libs', code ); - }, - }, - { - src: [ './assets/src/js/vendor/chart.min.js' ], - dest( code ) { - return minifyJsDest( `assets/js/vendor/chart`, code ); - }, - }, - ], -}; - -// adminSources().concat(frontendSources()).filter((value, index, self) => { -// return self.indexOf(value) === index; -// }).forEach((file) => { -// options.files.push({ -// src: [file], -// dest: function (code) { -// return minifyJsDest(file.replace(/\/assets\/src\//, '/assets/').replace(/(\.js$)/, ''), code); -// } -// }) -// }) - -const mergeAndCompressJs = new MergeIntoSingleFilePlugin( options ); - -module.exports = { - mergeAndCompressJs, -}; diff --git a/webpack.config.js b/webpack.config.js index dc4022ee8..9f7699eee 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,8 +1,15 @@ const path = require( 'path' ); -const webpack = require( 'webpack' ); -const tools = require( './tools/webpack' ); +const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); + +const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); +const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); +const MergeIntoSingleFilePlugin = require( 'webpack-merge-and-include-globally' ); +const LearnPressDependencyExtractionWebpackPlugin = require( './packages/dependecy-extraction-webpack-plugin' ); + +const isProduction = process.env.NODE_ENV === 'production'; module.exports = { + ...defaultConfig, entry: { './assets/js/dist/admin/editor/course': './assets/src/apps/js/admin/editor/course.js', './assets/js/dist/admin/editor/quiz': './assets/src/apps/js/admin/editor/quiz.js', @@ -15,34 +22,81 @@ module.exports = { './assets/js/dist/admin/pages/dashboard': './assets/src/apps/js/admin/pages/dashboard.js', './assets/js/dist/admin/pages/widgets': './assets/src/apps/js/admin/pages/widgets.js', './assets/js/dist/utils': './assets/src/js/utils/index.js', + + './assets/js/dist/js/data-controls': { + import: './assets/src/apps/js/data-controls.js', + library: { + name: [ 'LP', 'dataControls' ], + type: 'window', + }, + }, + './assets/js/dist/frontend/modal': { + import: './assets/src/apps/js/frontend/modal.js', + library: { + name: [ 'LP', 'modal' ], + type: 'window', + }, + }, + './assets/js/dist/frontend/quiz': { + import: './assets/src/apps/js/frontend/quiz.js', + library: { + name: [ 'LP', 'quiz' ], + type: 'window', + }, + }, + './assets/js/dist/frontend/lp-configs': { + import: './assets/src/apps/js/frontend/lp-configs.js', + library: { + name: [ 'LP', 'config' ], + type: 'window', + }, + }, + './assets/js/dist/frontend/question-types': { + import: './assets/src/apps/js/frontend/question-types.js', + library: { + name: [ 'LP', 'questionTypes' ], + type: 'window', + }, + }, + './assets/js/dist/frontend/courses': './assets/src/apps/js/frontend/courses.js', + './assets/js/dist/frontend/checkout': './assets/src/apps/js/frontend/checkout.js', + './assets/js/dist/frontend/single-course': './assets/src/apps/js/frontend/single-course.js', + './assets/js/dist/frontend/single-curriculum': './assets/src/apps/js/frontend/single-curriculum.js', + './assets/js/dist/frontend/lesson': './assets/src/apps/js/frontend/lesson.js', + './assets/js/dist/frontend/custom': './assets/src/apps/js/frontend/custom.js', + './assets/js/dist/frontend/profile': './assets/src/apps/js/frontend/profile.js', + './assets/js/dist/frontend/widgets': './assets/src/apps/js/frontend/widgets.js', + './assets/js/dist/blocks/index': './assets/src/apps/js/blocks/index.js', }, output: { path: path.resolve( __dirname ), - filename: 'production' === process.env.NODE_ENV ? '[name].min.js' : '[name].js', - }, - watch: false, - devtool: process.env.NODE_ENV === 'production' ? '' : 'source-map', - module: { - rules: [ - { - test: /\.js$/, - exclude: /(node_modules|bower_components)/, - use: { - loader: 'babel-loader', - options: { - presets: [ - '@babel/preset-env', - ], - plugins: [ - '@babel/plugin-transform-async-to-generator', - '@babel/plugin-proposal-object-rest-spread', - ], - }, - }, - }, - ], + filename: '[name]' + ( isProduction ? '.min.js' : '.js' ), }, plugins: [ - tools.mergeAndCompressJs, - ], + process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), + + // WP_NO_EXTERNALS global variable controls whether scripts' assets get + // generated, and the default externals set. + ! process.env.WP_NO_EXTERNALS && new DependencyExtractionWebpackPlugin(), + + new MergeIntoSingleFilePlugin( { + files: { + 'assets/js/vendor/plugins.all.js': [ + './assets/src/js/vendor/watch.js', + './assets/src/js/vendor/jquery/jquery-scrollTo.js', + './assets/src/js/vendor/jquery/jquery-timer.js', + './assets/src/js/vendor/jquery/jquery.tipsy.js', + ], + 'assets/js/vendor/vue/vue_libs.js': [ + './assets/src/js/vendor/vue/vue.js', + './assets/src/js/vendor/vue/vuex.js', + './assets/src/js/vendor/vue/vue-resource.js', + ], + }, + } ), + new LearnPressDependencyExtractionWebpackPlugin( { + namespace: '@learnpress', + library: 'LP', + } ), + ].filter( Boolean ), };