add webpack plugin to list licenses

This commit is contained in:
HF 2024-06-18 00:39:24 +02:00
parent 81634e0563
commit 3b57f74465
Signed by: hf
GPG Key ID: E7ABBB827162F9CC
11 changed files with 10411 additions and 19 deletions

1
.gitignore vendored
View File

@ -28,6 +28,5 @@ node_modules/
osm.tar.gz
clean.png
backup
backup/*
ips
pixelplanetmap.zip

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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",

View 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
View 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

View File

@ -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

File diff suppressed because it is too large Load Diff

42
scripts/zipDirectory.js Normal file
View 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,
)
}

View File

@ -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

View File

@ -18,6 +18,7 @@ process.chdir(__dirname);
const basePackageValues = {
name: pkg.name,
version: pkg.version,
license: pkg.license,
private: true,
engines: pkg.engines,
scripts: {