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
|
osm.tar.gz
|
||||||
clean.png
|
clean.png
|
||||||
backup
|
backup
|
||||||
backup/*
|
|
||||||
ips
|
ips
|
||||||
pixelplanetmap.zip
|
pixelplanetmap.zip
|
||||||
|
|
|
@ -3,16 +3,14 @@
|
||||||
|
|
||||||
[![Guilded](https://img.shields.io/badge/Discord-Support-blue.svg)](https://pixelplanet.fun/guilded)
|
[![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)
|
![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.
|
> 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/)
|
[![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.
|
To the 2nd anniversary of r/space, pixelplanet takes pixelgames to a new level. Place pixels, create pixelart and fight faction wars.
|
||||||
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.
|
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.
|
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.
|
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/" |
|
| BACKUP_DIR | mounted directory of backup server | "/mnt/backup/" |
|
||||||
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
|
| HOURLY_EVENT | run hourly void event on main canvas | 1 |
|
||||||
| USE_MAILER | enable to use mail sevicse | 0 |
|
| 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
|
#### 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",
|
"name": "pixelplanet",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"description": "Unlimited planet canvas for placing pixels",
|
"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",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/build.js",
|
"build": "node scripts/build.js",
|
||||||
|
@ -21,7 +28,6 @@
|
||||||
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
"lint": "cd $INIT_CWD && eslint --ext .jsx --ext .js",
|
||||||
"lint:src": "eslint --ext .jsx --ext .js src"
|
"lint:src": "eslint --ext .jsx --ext .js src"
|
||||||
},
|
},
|
||||||
"author": "HF <hf@example.com>",
|
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"defaults",
|
"defaults",
|
||||||
"not IE 11",
|
"not IE 11",
|
||||||
|
@ -97,6 +103,8 @@
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"generate-package-json-webpack-plugin": "^2.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",
|
"ttag-cli": "^1.10.12",
|
||||||
"webpack": "^5.89.0",
|
"webpack": "^5.89.0",
|
||||||
"webpack-bundle-analyzer": "^4.10.1",
|
"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 validate = require("ttag-cli/dist/src/commands/validate").default;
|
||||||
|
|
||||||
const minifyCss = require('./minifyCss');
|
const minifyCss = require('./minifyCss');
|
||||||
|
const zipDir = require('./zipDirectory');
|
||||||
const serverConfig = require('../webpack.config.server.js');
|
const serverConfig = require('../webpack.config.server.js');
|
||||||
const clientConfig = require('../webpack.config.client.js');
|
const clientConfig = require('../webpack.config.client.js');
|
||||||
|
|
||||||
|
@ -146,7 +147,7 @@ function validateLangs(langs) {
|
||||||
process.stdout.clearLine(0);
|
process.stdout.clearLine(0);
|
||||||
process.stdout.cursorTo(0);
|
process.stdout.cursorTo(0);
|
||||||
process.stdout.write(`i18n/${langFile} `);
|
process.stdout.write(`i18n/${langFile} `);
|
||||||
filePath = path.join(langDir, langFile);
|
const filePath = path.join(langDir, langFile);
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -338,6 +339,13 @@ async function build() {
|
||||||
|
|
||||||
if (!recursion) {
|
if (!recursion) {
|
||||||
console.log(`Finished building in ${(Date.now() - st) / 1000}s`);
|
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 {
|
} else {
|
||||||
console.log(`Worker done in ${(Date.now() - st) / 1000}s`);
|
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 process = require('process');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
|
const LicenseListWebpackPlugin = require('./scripts/LicenseListWebpackPlugin');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make sure we build in root dir
|
* make sure we build in root dir
|
||||||
|
@ -114,7 +115,7 @@ module.exports = ({
|
||||||
plugins: babelPlugins,
|
plugins: babelPlugins,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
path.resolve('scripts/TtagNonCacheableLoader.js'),
|
path.resolve('scripts', 'TtagNonCacheableLoader.js'),
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
path.resolve('src'),
|
path.resolve('src'),
|
||||||
|
@ -133,6 +134,9 @@ module.exports = ({
|
||||||
'process.env.NODE_ENV': development ? '"development"' : '"production"',
|
'process.env.NODE_ENV': development ? '"development"' : '"production"',
|
||||||
'process.env.BROWSER': true,
|
'process.env.BROWSER': true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Output license informations
|
||||||
|
new LicenseListWebpackPlugin({ outputDir: 'librejs', includeLicenseFiles: true, includeSourceFiles: true }),
|
||||||
|
|
||||||
// Webpack Bundle Analyzer
|
// Webpack Bundle Analyzer
|
||||||
// https://github.com/th0r/webpack-bundle-analyzer
|
// https://github.com/th0r/webpack-bundle-analyzer
|
||||||
|
|
|
@ -18,6 +18,7 @@ process.chdir(__dirname);
|
||||||
const basePackageValues = {
|
const basePackageValues = {
|
||||||
name: pkg.name,
|
name: pkg.name,
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
|
license: pkg.license,
|
||||||
private: true,
|
private: true,
|
||||||
engines: pkg.engines,
|
engines: pkg.engines,
|
||||||
scripts: {
|
scripts: {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user