add webpack plugin to list licenses
This commit is contained in:
parent
81634e0563
commit
3b57f74465
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,5 @@ node_modules/
|
|||
osm.tar.gz
|
||||
clean.png
|
||||
backup
|
||||
backup/*
|
||||
ips
|
||||
pixelplanetmap.zip
|
||||
|
|
|
@ -3,16 +3,14 @@
|
|||
|
||||
[![Guilded](https://img.shields.io/badge/Discord-Support-blue.svg)](https://pixelplanet.fun/guilded)
|
||||
|
||||
Official repository of [pixelplanet.fun](http://www.pixelplanet.fun).
|
||||
|
||||
![videothumb](promotion/videothumb.gif)
|
||||
|
||||
> Our translations are hosted [on Weblate](https://hosted.weblate.org/projects/pixelplanet), information on how to contribute is available under [i18n](./i18n). We very much appreciate any help.
|
||||
|
||||
[![Translation status](https://hosted.weblate.org/widget/pixelplanet/svg-badge.svg)](https://hosted.weblate.org/engage/pixelplanet/)
|
||||
|
||||
To the 2nd anniversary of r/space, pixelplanet takes pixelgames to a new level. Place pixels, create pixelart and fight faction wars on pixelplanet.fun.
|
||||
Pixelplanet is a 65k x 65k large canvas that is a map of the world and can also be seen as 3d globe, you can place pixels where ever you want, build an island, take over another country with a flag or just create pixelart.
|
||||
To the 2nd anniversary of r/space, pixelplanet takes pixelgames to a new level. Place pixels, create pixelart and fight faction wars.
|
||||
Pixelplanet presents a 65k x 65k large canvas that is a map of the world and can also be seen as 3d globe, you can place pixels where ever you want, build an island, take over another country with a flag or just create pixelart.
|
||||
30 well chosen colors (decided by polls within the community) are available and you can place a pixel every 3s on an empty space, and 5s on an already set pixel. But pixels can be stacked up to a minute, so you don't have to wait every time.
|
||||
|
||||
Pixelplanet receives regular updates and launches events, like a zero second cooldown day on r/place anniversary. We are driven by our community, because placing pixels is more fun together.
|
||||
|
@ -84,7 +82,7 @@ Configuration takes place in the environment variables that are defined in ecosy
|
|||
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
|
||||
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
|
||||
| USE_MAILER | enable to use mail sevicse | 0 |
|
||||
| MAIL_ADDRESS | email address for sending mails | "noreply@pixelplanet.fun" |
|
||||
| MAIL_ADDRESS | email address for sending mails | "noreply@example.com" |
|
||||
|
||||
#### Social Media Configuration
|
||||
|
||||
|
|
1388
package-lock.json
generated
1388
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -1,11 +1,18 @@
|
|||
{
|
||||
"name": "PixelPlanet",
|
||||
"version": "1.1.0",
|
||||
"name": "pixelplanet",
|
||||
"version": "1.2.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=18"
|
||||
},
|
||||
"description": "Unlimited planet canvas for placing pixels",
|
||||
"homepage": "https://git.pixelplanet.fun/ppfun/pixelplanet",
|
||||
"author": "HF <hf@pixelplanet.fun>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.pixelplanet.fun/ppfun/pixelplanet.git"
|
||||
},
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"build": "node scripts/build.js",
|
||||
|
@ -21,7 +28,6 @@
|
|||
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
||||
"lint:src": "eslint --ext .jsx --ext .js src"
|
||||
},
|
||||
"author": "HF <hf@example.com>",
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
"not IE 11",
|
||||
|
@ -97,6 +103,8 @@
|
|||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"generate-package-json-webpack-plugin": "^2.6.0",
|
||||
"node-zip-cli": "https://github.com/matteosacchetto/node-zip-cli/releases/download/v0.6.0/node-zip-cli-0.6.0.tgz",
|
||||
"spdx-correct": "^3.2.0",
|
||||
"ttag-cli": "^1.10.12",
|
||||
"webpack": "^5.89.0",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
|
|
656
scripts/LicenseListWebpackPlugin.js
Normal file
656
scripts/LicenseListWebpackPlugin.js
Normal file
|
@ -0,0 +1,656 @@
|
|||
/*
|
||||
* Creates json file of used licenses in a webpack bundle
|
||||
*
|
||||
* Make sure that a licenses.json file is present in the same directory as
|
||||
* this plugin, you can get it from:
|
||||
* https://github.com/spdx/license-list-data/blob/main/json/licenses.json
|
||||
*
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const spdxCorrect = require('spdx-correct');
|
||||
const spdxParse = require('spdx-expression-parse');
|
||||
|
||||
const PLUGIN_NAME = 'LicenseListWebpackPlugin';
|
||||
|
||||
const licensesURLMapping = {
|
||||
'Apache-2.0': 'http://www.apache.org/licenses/LICENSE-2.0',
|
||||
'Artistic-2.0': 'http://www.perlfoundation.org/artistic_license_2_0',
|
||||
'BSL-1.0': 'http://www.boost.org/LICENSE_1_0.txt',
|
||||
'BSD-3-Clause': 'http://opensource.org/licenses/BSD-3-Clause',
|
||||
'CPAL-1.0': 'http://opensource.org/licenses/cpal_1.0',
|
||||
'CC0-1.0': 'http://creativecommons.org/publicdomain/zero/1.0/legalcode',
|
||||
'EPL-1.0': 'http://www.eclipse.org/legal/epl-v10.html',
|
||||
'MIT': 'http://www.jclark.com/xml/copying.txt',
|
||||
'BSD-2-Clause-FreeBSD': 'http://www.freebsd.org/copyright/freebsd-license.html',
|
||||
'GPL-2.0-only': 'http://www.gnu.org/licenses/gpl-2.0.html',
|
||||
'GPL-2.0-or-later': 'http://www.gnu.org/licenses/gpl-2.0.html',
|
||||
'GPL-2.0+': 'http://www.gnu.org/licenses/gpl-2.0.html',
|
||||
'GPL-2.0': 'http://www.gnu.org/licenses/gpl-2.0.html',
|
||||
'GPL-3.0-only': 'http://www.gnu.org/licenses/gpl-3.0.html',
|
||||
'GPL-3.0-or-later': 'http://www.gnu.org/licenses/gpl-3.0.html',
|
||||
'GPL-3.0+': 'http://www.gnu.org/licenses/gpl-3.0.html',
|
||||
'GPL-3.0': 'http://www.gnu.org/licenses/gpl-3.0.html',
|
||||
'LGPL-2.1-only': 'http://www.gnu.org/licenses/lgpl-2.1.html',
|
||||
'LGPL-2.1-or-later': 'http://www.gnu.org/licenses/lgpl-2.1.html',
|
||||
'LGPL-2.1+': 'http://www.gnu.org/licenses/lgpl-2.1.html',
|
||||
'LGPL-2.1': 'http://www.gnu.org/licenses/lgpl-2.1.html',
|
||||
'LGPL-3.0-only': 'http://www.gnu.org/licenses/lgpl-3.0.html',
|
||||
'LGPL-3.0-or-later': 'http://www.gnu.org/licenses/lgpl-3.0.html',
|
||||
'LGPL-3.0+': 'http://www.gnu.org/licenses/lgpl-3.0.html',
|
||||
'LGPL-3.0': 'http://www.gnu.org/licenses/lgpl-3.0.html',
|
||||
'AGPL-3.0-only': 'http://www.gnu.org/licenses/agpl-3.0.html',
|
||||
'AGPL-3.0-or-later': 'http://www.gnu.org/licenses/agpl-3.0.html',
|
||||
'AGPL-3.0+': 'http://www.gnu.org/licenses/agpl-3.0.html',
|
||||
'AGPL-3.0': 'http://www.gnu.org/licenses/agpl-3.0.html',
|
||||
'ISC': 'https://www.isc.org/downloads/software-support-policy/isc-license/',
|
||||
'MPL-2.0': 'http://www.mozilla.org/MPL/2.0',
|
||||
'UPL-1.0': 'https://oss.oracle.com/licenses/upl/',
|
||||
'WTFPL': 'http://www.wtfpl.net/txt/copying/',
|
||||
'Unlicense': 'http://unlicense.org/UNLICENSE',
|
||||
'X11 License': 'http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3',
|
||||
'XFree86-1.1': 'http://www.xfree86.org/current/LICENSE4.html',
|
||||
};
|
||||
|
||||
/*
|
||||
* load json file
|
||||
* @param filepath
|
||||
* @return parsed json object or null if failed
|
||||
*/
|
||||
function readJSONFile(filepath) {
|
||||
if (fs.existsSync(filepath)) {
|
||||
return JSON.parse(fs.readFileSync(filepath).toString('utf8'));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve %{variable} in string templates
|
||||
* @param template
|
||||
* @param values values to fill in { variable: value,... }
|
||||
*/
|
||||
function resolveStringTemplate(template, values) {
|
||||
let text = '';
|
||||
let pStart = 0;
|
||||
let isInside = 0;
|
||||
for (let i = 0; i < template.length; i += 1) {
|
||||
const char = template[i];
|
||||
if (isInside === 2) {
|
||||
if (char === '}') {
|
||||
isInside = 0;
|
||||
text += values[template.slice(pStart, i)];
|
||||
pStart = i + 1;
|
||||
}
|
||||
} else if (char === '%') {
|
||||
isInside = 1;
|
||||
} else if (isInside === 1) {
|
||||
if (char === '{') {
|
||||
isInside = 2;
|
||||
text += template.slice(pStart, i - 1);
|
||||
pStart = i + 1;
|
||||
} else {
|
||||
isInside = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
text += template.slice(pStart);
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
* deep merge two objects
|
||||
*/
|
||||
function deepMerge(obj1, obj2) {
|
||||
for (let key in obj2) {
|
||||
if (obj2.hasOwnProperty(key)) {
|
||||
if (obj2[key] instanceof Object && obj1[key] instanceof Object) {
|
||||
obj1[key] = deepMerge(obj1[key], obj2[key]);
|
||||
} else {
|
||||
obj1[key] = obj2[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj1;
|
||||
}
|
||||
|
||||
class LicenseListWebpackPlugin {
|
||||
chunkIdToName = {};
|
||||
chunkNameToJsAsset = {};
|
||||
packageJsonCache = {};
|
||||
packageTextFiles = {};
|
||||
copiedFiles = new Map();
|
||||
spdxLicenses = {};
|
||||
logger = console;
|
||||
exclude = [];
|
||||
override = {};
|
||||
srcReplace;
|
||||
outputDir;
|
||||
outputPath;
|
||||
sourcesOutputDir;
|
||||
sourcesOutputPath;
|
||||
sourcesPublicPath;
|
||||
outputFile;
|
||||
sourceTemplates;
|
||||
output = {};
|
||||
includeLicenseFiles;
|
||||
includeSourceFiles;
|
||||
publicPathOverride;
|
||||
processOutput;
|
||||
sourcesDir = 'sources';
|
||||
static srcExtsRegexp = new RegExp('^.*.([js,ts,jsx,coffee,lua])$');
|
||||
static filesToCopy = ['license', 'copying', 'authors', 'code_of_conduct'];
|
||||
static filePathCleanUpRegEx = /^([.\/]*node_modules|\.)\//;
|
||||
static modulePathCleanUpRegEx = /^([.\/]*node_modules\/[^\/]*|\.)\//;
|
||||
|
||||
/*
|
||||
* @param options {
|
||||
* outputDir,
|
||||
* filename,
|
||||
* append,
|
||||
* exclude: [],
|
||||
* srcReplace: {},
|
||||
* override: {},
|
||||
* includeLicenseFiles: boolean,
|
||||
* includeSourceFiles: boolean,
|
||||
* souces: {},
|
||||
* processOutput: function,
|
||||
* }
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
this.outputDir = options.outputDir || 'dist';
|
||||
this.sourcesOutputDir = path.join(options.outputDir, this.sourcesDir);
|
||||
this.outputFilename = options.filename || 'licenses.json';
|
||||
this.sourceTemplates = options.sources || {};
|
||||
this.publicPathOverride = options.publicPath;
|
||||
this.includeLicenseFiles = options.includeLicenseFiles;
|
||||
this.includeSourceFiles = options.includeSourceFiles;
|
||||
this.srcReplace = options.srcReplace || {};
|
||||
this.processOutput = options.processOutput;
|
||||
// populate module prefix patterns to exclude
|
||||
if (Array.isArray(options.exclude)) {
|
||||
this.options['exclude'].forEach(toExclude => {
|
||||
if (!toExclude.startsWith('.')) {
|
||||
this.exclude.push('./' + path.join('node_modules', toExclude));
|
||||
} else {
|
||||
this.exclude.push(toExclude);
|
||||
}
|
||||
});
|
||||
}
|
||||
// populate license Override
|
||||
if (options.override) {
|
||||
for (const [srcFilePrefixKey, moduleOverride] of Object.entries(
|
||||
options.override,
|
||||
)) {
|
||||
const srcFilePrefix = (srcFilePrefixKey.startsWith('.'))
|
||||
? srcFilePrefixKey
|
||||
: './' + path.join('node_modules', srcFilePrefixKey);
|
||||
if (moduleOverride.license) {
|
||||
const parsedSpdxLicenses = this.parseSpdxLicenseExpression(
|
||||
moduleOverride.license, `file ${srcFilePrefixKey}`,
|
||||
);
|
||||
moduleOverride.licenses = this.spdxToWebLabelsLicenses(
|
||||
parsedSpdxLicenses,
|
||||
);
|
||||
}
|
||||
this.override[srcFilePrefix] = moduleOverride;
|
||||
}
|
||||
}
|
||||
// spdx licenses informations
|
||||
const spdxLicenseFile = readJSONFile(
|
||||
path.resolve(__dirname, 'licenses.json'),
|
||||
);
|
||||
if (spdxLicenseFile?.licenses) {
|
||||
spdxLicenseFile.licenses.forEach((l) => {
|
||||
if (licensesURLMapping[l.licenseId]) {
|
||||
l.reference = licensesURLMapping[l.licenseId];
|
||||
}
|
||||
this.spdxLicenses[l.licenseId] = l;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static findPackageJsonPath(srcFilePath) {
|
||||
const pathSplit = srcFilePath.split('/');
|
||||
let packageJsonPath;
|
||||
for (let i = 3; i < pathSplit.length; ++i) {
|
||||
packageJsonPath = path.join(...pathSplit.slice(0, i), 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return packageJsonPath;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse output object to HTML
|
||||
*/
|
||||
static generateHTML(output) {
|
||||
let columns = [];
|
||||
for (const [key, value] of Object.entries(output)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
findTextFile(packageJsonDir, name) {
|
||||
let packageTextFiles = this.packageTextFiles[name];
|
||||
if (!packageTextFiles) {
|
||||
packageTextFiles = {};
|
||||
this.packageTextFiles[name] = packageTextFiles;
|
||||
}
|
||||
if (!packageTextFiles.hasOwnProperty(packageJsonDir)) {
|
||||
let foundTextFile;
|
||||
fs.readdirSync(packageJsonDir).forEach(file => {
|
||||
if (foundTextFile) {
|
||||
return;
|
||||
}
|
||||
if (file.toLowerCase().startsWith(name)) {
|
||||
foundTextFile = path.join(packageJsonDir, file);
|
||||
}
|
||||
});
|
||||
packageTextFiles[packageJsonDir] = foundTextFile;
|
||||
}
|
||||
return packageTextFiles[packageJsonDir];
|
||||
}
|
||||
|
||||
copyTextFile(textFilePath) {
|
||||
if (!textFilePath) return '';
|
||||
const ext = (textFilePath.indexOf('.') === -1) ? '.txt' : '';
|
||||
return this.copyFileToOutputPath(textFilePath, ext);
|
||||
}
|
||||
|
||||
parsePackageJson(packageJsonPath) {
|
||||
if (!this.packageJsonCache.hasOwnProperty(packageJsonPath)) {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString('utf8'));
|
||||
|
||||
packageJson.licenses = this.extractLicenseInformation(packageJson);
|
||||
const licenseDir = path.join(...packageJsonPath.split('/').slice(0, -1));
|
||||
if (this.includeLicenseFiles) {
|
||||
packageJson.files = LicenseListWebpackPlugin.filesToCopy.map(
|
||||
(filename) => this.findTextFile(licenseDir, filename),
|
||||
).filter((f) => f);
|
||||
} else {
|
||||
delete packageJson.files;
|
||||
}
|
||||
const repositoryUrl = packageJson.repository?.url;
|
||||
if (repositoryUrl) packageJson.repository = repositoryUrl;
|
||||
this.packageJsonCache[packageJsonPath] = packageJson;
|
||||
}
|
||||
return this.packageJsonCache[packageJsonPath];
|
||||
}
|
||||
|
||||
parseSpdxLicenseExpression(spdxLicenseExpression, context) {
|
||||
let parsedLicense;
|
||||
try {
|
||||
parsedLicense = spdxParse(spdxCorrect(spdxLicenseExpression));
|
||||
if (spdxLicenseExpression.indexOf('AND') !== -1) {
|
||||
this.logger.warn(`The SPDX license expression '${spdxLicenseExpression}' associated to ${context} ` +
|
||||
'contains an AND operator, this is currently not properly handled and erroneous ' +
|
||||
'licenses information may be provided to LibreJS');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.warn(`Unable to parse the SPDX license expression '${spdxLicenseExpression}' associated to ${context}.`);
|
||||
this.logger.warn('Some generated JavaScript assets may be blocked by LibreJS due to missing license information.');
|
||||
parsedLicense = {'license': spdxLicenseExpression};
|
||||
}
|
||||
return parsedLicense;
|
||||
}
|
||||
|
||||
spdxToWebLabelsLicense(spdxLicenceId) {
|
||||
const licenseInfo = this.spdxLicenses[spdxLicenceId];
|
||||
if (licenseInfo) {
|
||||
if (!licenseInfo.isFsfLibre) {
|
||||
this.logger.info(`License '${spdxLicenceId}' is not a Free license according to the FSF.`);
|
||||
}
|
||||
return {
|
||||
name: spdxLicenceId,
|
||||
url: licenseInfo.reference,
|
||||
};
|
||||
}
|
||||
this.logger.warn(`Unable to associate the SPDX license identifier '${spdxLicenceId}' to a LibreJS supported license.`);
|
||||
this.logger.warn('Some generated JavaScript assets may be blocked by LibreJS due to missing license information.');
|
||||
return {
|
||||
'name': spdxLicenceId,
|
||||
'url': '',
|
||||
};
|
||||
}
|
||||
|
||||
spdxToWebLabelsLicenses(spdxLicenses) {
|
||||
// This method simply extracts all referenced licenses in the SPDX expression
|
||||
// regardless of their combinations.
|
||||
// TODO: Handle licenses combination properly once LibreJS has a spec for it.
|
||||
let ret = [];
|
||||
if (spdxLicenses.hasOwnProperty('license')) {
|
||||
ret.push(this.spdxToWebLabelsLicense(spdxLicenses['license']));
|
||||
} else if (spdxLicenses.hasOwnProperty('left')) {
|
||||
if (spdxLicenses['left'].hasOwnProperty('license')) {
|
||||
const licenseData = this.spdxToWebLabelsLicense(spdxLicenses['left']['license']);
|
||||
ret.push(licenseData);
|
||||
} else {
|
||||
ret = ret.concat(this.spdxToWebLabelsLicenses(spdxLicenses['left']));
|
||||
}
|
||||
ret = ret.concat(this.spdxToWebLabelsLicenses(spdxLicenses['right']));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
extractLicenseInformation(packageJson) {
|
||||
let spdxLicenseExpression;
|
||||
if (packageJson.hasOwnProperty('license')) {
|
||||
spdxLicenseExpression = packageJson['license'];
|
||||
} else if (packageJson.hasOwnProperty('licenses')) {
|
||||
// for node packages using deprecated licenses property
|
||||
const licenses = packageJson['licenses'];
|
||||
if (Array.isArray(licenses)) {
|
||||
const l = [];
|
||||
licenses.forEach(license => {
|
||||
l.push(license['type']);
|
||||
});
|
||||
spdxLicenseExpression = l.join(' OR ');
|
||||
} else {
|
||||
spdxLicenseExpression = licenses['type'];
|
||||
}
|
||||
}
|
||||
const parsedSpdxLicenses = this.parseSpdxLicenseExpression(spdxLicenseExpression,
|
||||
`module ${packageJson['name']}`);
|
||||
return this.spdxToWebLabelsLicenses(parsedSpdxLicenses);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy source or license file to output directory
|
||||
* @param srcFilePath full path to file
|
||||
* @param ext file extionsion to append (dot included like '.txt')
|
||||
* @return public path if successful, null if not
|
||||
*/
|
||||
copyFileToOutputPath(srcFilePath, ext = '') {
|
||||
let publicFilePath = this.copiedFiles.get(srcFilePath);
|
||||
if (publicFilePath) {
|
||||
return publicFilePath;
|
||||
}
|
||||
if (srcFilePath.indexOf('://') !== -1 || !fs.existsSync(srcFilePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine output bath based on folder within package
|
||||
let destPath = srcFilePath.replace(
|
||||
LicenseListWebpackPlugin.filePathCleanUpRegEx,
|
||||
'',
|
||||
) + ext;
|
||||
|
||||
publicFilePath = path.join(this.sourcesPublicPath, destPath);
|
||||
const destDir = path.join(this.sourcesOutputPath, ...destPath.split('/').slice(0, -1));
|
||||
if (!fs.existsSync(destDir)) {
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.copyFileSync(
|
||||
srcFilePath, path.join(this.sourcesOutputPath, destPath),
|
||||
);
|
||||
this.copiedFiles.set(srcFilePath, publicFilePath);
|
||||
return publicFilePath;
|
||||
}
|
||||
|
||||
getPackageInformation(srcFilePath) {
|
||||
let name;
|
||||
let version;
|
||||
let licenses;
|
||||
let repository;
|
||||
let files;
|
||||
let homepage;
|
||||
let sources;
|
||||
|
||||
// find and parse the corresponding package.json file
|
||||
let packageJsonPath;
|
||||
const nodeModule = srcFilePath.startsWith('./node_modules/');
|
||||
if (nodeModule) {
|
||||
packageJsonPath = LicenseListWebpackPlugin.findPackageJsonPath(srcFilePath);
|
||||
} else {
|
||||
packageJsonPath = './package.json';
|
||||
}
|
||||
({
|
||||
name,
|
||||
version,
|
||||
homepage,
|
||||
licenses,
|
||||
repository,
|
||||
files,
|
||||
} = this.parsePackageJson(packageJsonPath));
|
||||
|
||||
// custom overrides
|
||||
for (const srcFilePrefix in this.override) {
|
||||
if (srcFilePath.startsWith(srcFilePrefix)) {
|
||||
const moduleOverride = this.override[srcFilePrefix];
|
||||
if (moduleOverride.replace) {
|
||||
name = undefined;
|
||||
version = undefined;
|
||||
homepage = undefined,
|
||||
licenses = undefined;
|
||||
repository = undefined;
|
||||
files = undefined;
|
||||
}
|
||||
const {
|
||||
name: overridename,
|
||||
version: overrideVersion,
|
||||
homepage: overrideHomepage,
|
||||
repository: overrideRepository,
|
||||
files: overrideFiles,
|
||||
licenses: overrideLicenses,
|
||||
} = moduleOverride;
|
||||
if (overridename) name = overridename;
|
||||
if (overrideVersion) version = overrideVersion;
|
||||
if (overrideHomepage) version = overrideHomepage;
|
||||
if (overrideRepository) repository = overrideRepository;
|
||||
if (overrideLicenses) {
|
||||
if (!Array.isArray(licenses)) licenses = [];
|
||||
licenses = licenses.concat(overrideLicenses);
|
||||
}
|
||||
if (overrideFiles) {
|
||||
if (!Array.isArray(licenses)) files = [];
|
||||
files = files.concat(overrideFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.includeSourceFiles) {
|
||||
let replaceTemplate = this.srcReplace[name];
|
||||
if (replaceTemplate) {
|
||||
if (!Array.isArray(replaceTemplate)) {
|
||||
replaceTemplate = [replaceTemplate];
|
||||
}
|
||||
sources = replaceTemplate.map(
|
||||
(t) => resolveStringTemplate(t, { name, version }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
version,
|
||||
homepage,
|
||||
licenses,
|
||||
repository,
|
||||
files,
|
||||
sources,
|
||||
};
|
||||
}
|
||||
|
||||
addModuleToOutput(srcFilePath, chunkJsAsset, packageInformation) {
|
||||
const {
|
||||
name,
|
||||
version,
|
||||
homepage,
|
||||
licenses,
|
||||
repository,
|
||||
sources,
|
||||
files,
|
||||
} = packageInformation;
|
||||
// init the chunk to source files mapping if needed
|
||||
if (!this.output.hasOwnProperty(chunkJsAsset)) {
|
||||
this.output[chunkJsAsset] = [];
|
||||
}
|
||||
|
||||
const assetOutput = this.output[chunkJsAsset];
|
||||
let packageOutput = assetOutput.find(
|
||||
(m) => m.name === name,
|
||||
);
|
||||
|
||||
if (!packageOutput) {
|
||||
let parsedFiles = [];
|
||||
if (Array.isArray(files)) {
|
||||
for (let i = 0; i < files.length; i += 1) {
|
||||
const filePath = files[i];
|
||||
if (filePath.id) {
|
||||
parsedFiles.push(filePath);
|
||||
} else if (typeof filePath === 'string') {
|
||||
parsedFiles.push({
|
||||
id: path.parse(filePath).name.toUpperCase(),
|
||||
url: this.copyTextFile(filePath),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packageOutput = {
|
||||
name,
|
||||
url: homepage,
|
||||
version,
|
||||
licenses,
|
||||
};
|
||||
if (sources) {
|
||||
packageOutput.sources = sources;
|
||||
} else if (this.includeSourceFiles) {
|
||||
packageOutput.sources = [];
|
||||
}
|
||||
if (!this.includeSourceFiles || this.srcReplace[name]) {
|
||||
packageOutput.modules = [];
|
||||
}
|
||||
if (parsedFiles.length) packageOutput.files = parsedFiles;
|
||||
if (repository) packageOutput.repository = repository;
|
||||
assetOutput.push(packageOutput);
|
||||
}
|
||||
|
||||
const id = srcFilePath.replace(
|
||||
LicenseListWebpackPlugin.modulePathCleanUpRegEx,
|
||||
'',
|
||||
);
|
||||
const module = { name: id };
|
||||
let moduleAddArray;
|
||||
if (!this.includeSourceFiles || this.srcReplace[name]) {
|
||||
moduleAddArray = packageOutput.modules;
|
||||
} else {
|
||||
moduleAddArray = packageOutput.sources;
|
||||
module.url = this.copyFileToOutputPath(srcFilePath);
|
||||
}
|
||||
|
||||
if (!moduleAddArray.some((m) => m.id === id)) {
|
||||
moduleAddArray.push(module);
|
||||
}
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
this.logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
|
||||
|
||||
compiler.hooks.done.tapAsync(PLUGIN_NAME, (statsObj, callback) => {
|
||||
// https://webpack.js.org/api/stats/
|
||||
const stats = statsObj.toJson();
|
||||
this.outputPath = path.join(stats.outputPath, this.outputDir);
|
||||
const publicPath = this.publicPathOverride || stats.publicPath;
|
||||
this.sourcesOutputPath = path.join(stats.outputPath, this.sourcesOutputDir);
|
||||
this.sourcesPublicPath = path.join(publicPath, this.sourcesOutputDir);
|
||||
|
||||
if (!fs.existsSync(this.outputPath)) {
|
||||
fs.mkdirSync(this.outputPath, { recursive: true });
|
||||
}
|
||||
|
||||
stats.assets.forEach(asset => {
|
||||
for (let i = 0; i < asset.chunks.length; ++i) {
|
||||
this.chunkIdToName[asset.chunks[i]] = asset.chunkNames[i];
|
||||
}
|
||||
});
|
||||
|
||||
// map each generated webpack chunk to its js asset
|
||||
Object.keys(stats.assetsByChunkName).forEach((chunkName, i) => {
|
||||
if (Array.isArray(stats.assetsByChunkName[chunkName])) {
|
||||
for (const asset of stats.assetsByChunkName[chunkName]) {
|
||||
if (asset.endsWith('.js')) {
|
||||
this.chunkNameToJsAsset[chunkName] = asset;
|
||||
this.chunkNameToJsAsset[i] = asset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (stats.assetsByChunkName[chunkName].endsWith('.js')) {
|
||||
this.chunkNameToJsAsset[chunkName] = stats.assetsByChunkName[chunkName];
|
||||
this.chunkNameToJsAsset[i] = stats.assetsByChunkName[chunkName];
|
||||
}
|
||||
});
|
||||
|
||||
// iterate on all bundled webpack modules
|
||||
stats.modules.forEach((mod) => {
|
||||
let srcFilePath = mod.name;
|
||||
const size = mod.size;
|
||||
|
||||
// do not process non js related modules
|
||||
if (!LicenseListWebpackPlugin.srcExtsRegexp.test(srcFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not process modules unrelated to a source file
|
||||
if (!srcFilePath.startsWith('./')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not process modules in the exclusion list
|
||||
for (const toExclude of this.exclude) {
|
||||
if (srcFilePath.startsWith(toExclude)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// remove webpack loader call if any
|
||||
const loaderEndPos = srcFilePath.indexOf('!');
|
||||
if (loaderEndPos !== -1) {
|
||||
srcFilePath = srcFilePath.slice(loaderEndPos + 1);
|
||||
}
|
||||
|
||||
// check if the source file needs to be replaces
|
||||
if (this.srcReplace.hasOwnProperty(srcFilePath)) {
|
||||
srcFilePath = this.srcReplace[srcFilePath];
|
||||
}
|
||||
|
||||
const packageInformation = this.getPackageInformation(srcFilePath);
|
||||
|
||||
// iterate on all chunks containing the module
|
||||
mod.chunks.forEach(chunk => {
|
||||
const chunkName = this.chunkIdToName[chunk];
|
||||
const chunkJsAsset = publicPath + this.chunkNameToJsAsset[chunkName];
|
||||
this.addModuleToOutput(srcFilePath, chunkJsAsset, packageInformation);
|
||||
});
|
||||
});
|
||||
|
||||
for (const assetOutput of Object.values(this.output)) {
|
||||
for (const packageOutput of Object.values(assetOutput)) {
|
||||
if (packageOutput.modules) {
|
||||
packageOutput.modules.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
if (packageOutput.sources) {
|
||||
packageOutput.sources.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate the output file
|
||||
if (this.processOutput) {
|
||||
this.output = this.processOutput(this.output);
|
||||
}
|
||||
if (typeof this.output !== 'string') {
|
||||
this.output = JSON.stringify(this.output);
|
||||
}
|
||||
const weblabelsJsonFile = path.join(this.outputPath, this.outputFilename);
|
||||
fs.writeFileSync(weblabelsJsonFile, this.output);
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LicenseListWebpackPlugin;
|
27
scripts/README.md
Normal file
27
scripts/README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Scripts
|
||||
|
||||
Scripts needed for building pixelplanet
|
||||
|
||||
## build.js
|
||||
|
||||
Script for building pixelplanet. Allows parallelization of language builds and checks integrity of translation files.
|
||||
|
||||
## licenses.json
|
||||
|
||||
Download of the [SPDX License List](https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json).
|
||||
|
||||
## TtagPoLoader.js
|
||||
|
||||
Webpack Loader for `.po` files. Similar to [ttag](https://github.com/ttag-org/ttag-po-loader).
|
||||
|
||||
## TtagNonCachableLoader.js
|
||||
|
||||
Webpack loader that sets files that include ttag translations as non-cachable.
|
||||
|
||||
## LicenseListWebpackPlugin.js
|
||||
|
||||
Creates a list of used licenses of packages in a webpack bundle.
|
||||
|
||||
## minifyCss.js
|
||||
|
||||
Minifies CSS files
|
|
@ -11,6 +11,7 @@ const webpack = require('webpack');
|
|||
const validate = require("ttag-cli/dist/src/commands/validate").default;
|
||||
|
||||
const minifyCss = require('./minifyCss');
|
||||
const zipDir = require('./zipDirectory');
|
||||
const serverConfig = require('../webpack.config.server.js');
|
||||
const clientConfig = require('../webpack.config.client.js');
|
||||
|
||||
|
@ -146,7 +147,7 @@ function validateLangs(langs) {
|
|||
process.stdout.clearLine(0);
|
||||
process.stdout.cursorTo(0);
|
||||
process.stdout.write(`i18n/${langFile} `);
|
||||
filePath = path.join(langDir, langFile);
|
||||
const filePath = path.join(langDir, langFile);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -338,6 +339,13 @@ async function build() {
|
|||
|
||||
if (!recursion) {
|
||||
console.log(`Finished building in ${(Date.now() - st) / 1000}s`);
|
||||
/*
|
||||
console.log('Archiving Source');
|
||||
await zipDir(
|
||||
path.resolve(__dirname, '..'),
|
||||
path.resolve(__dirname, '..', 'dist', 'public', `source.zip`),
|
||||
);
|
||||
*/
|
||||
} else {
|
||||
console.log(`Worker done in ${(Date.now() - st) / 1000}s`);
|
||||
}
|
||||
|
|
8275
scripts/licenses.json
Normal file
8275
scripts/licenses.json
Normal file
File diff suppressed because it is too large
Load Diff
42
scripts/zipDirectory.js
Normal file
42
scripts/zipDirectory.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* ZIP a directory while adhering to .gitignore
|
||||
*/
|
||||
const path = require('path');
|
||||
|
||||
async function zipDir(dir, outputFile) {
|
||||
const [
|
||||
{ create_zip },
|
||||
{ list_entries },
|
||||
{ is_windows },
|
||||
] = await Promise.all([
|
||||
import('node-zip-cli/dist/core/zip.mjs'),
|
||||
import('node-zip-cli/dist/core/walk.mjs'),
|
||||
import('node-zip-cli/dist/core/constants.mjs'),
|
||||
]);
|
||||
const [
|
||||
unique_list,
|
||||
conflicting_list,
|
||||
absolute_path_to_clean_entry_with_mode,
|
||||
] = await list_entries(
|
||||
[path.normalize(dir)],
|
||||
is_windows,
|
||||
'full', // keepParent
|
||||
'none', // symlink
|
||||
false, // allowGit
|
||||
[], // exclude
|
||||
'none', // disableIgnore
|
||||
);
|
||||
if (conflicting_list.length) {
|
||||
throw new Error(
|
||||
`Can not zip reqpository because of conflicting files ${conflicting_list.join(',')}`,
|
||||
);
|
||||
}
|
||||
await create_zip(
|
||||
outputFile,
|
||||
unique_list,
|
||||
absolute_path_to_clean_entry_with_mode,
|
||||
unique_list.filter((el) => el.type !== 'directory').length,
|
||||
6, // compression level (0 - 9)
|
||||
is_windows,
|
||||
)
|
||||
}
|
|
@ -7,6 +7,7 @@ const path = require('path');
|
|||
const process = require('process');
|
||||
const webpack = require('webpack');
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
const LicenseListWebpackPlugin = require('./scripts/LicenseListWebpackPlugin');
|
||||
|
||||
/*
|
||||
* make sure we build in root dir
|
||||
|
@ -114,7 +115,7 @@ module.exports = ({
|
|||
plugins: babelPlugins,
|
||||
},
|
||||
},
|
||||
path.resolve('scripts/TtagNonCacheableLoader.js'),
|
||||
path.resolve('scripts', 'TtagNonCacheableLoader.js'),
|
||||
],
|
||||
include: [
|
||||
path.resolve('src'),
|
||||
|
@ -133,6 +134,9 @@ module.exports = ({
|
|||
'process.env.NODE_ENV': development ? '"development"' : '"production"',
|
||||
'process.env.BROWSER': true,
|
||||
}),
|
||||
|
||||
// Output license informations
|
||||
new LicenseListWebpackPlugin({ outputDir: 'librejs', includeLicenseFiles: true, includeSourceFiles: true }),
|
||||
|
||||
// Webpack Bundle Analyzer
|
||||
// https://github.com/th0r/webpack-bundle-analyzer
|
||||
|
|
|
@ -18,6 +18,7 @@ process.chdir(__dirname);
|
|||
const basePackageValues = {
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
license: pkg.license,
|
||||
private: true,
|
||||
engines: pkg.engines,
|
||||
scripts: {
|
||||
|
|
Loading…
Reference in New Issue
Block a user