Răsfoiți Sursa

chore: drop plaid

Gerald 2 ani în urmă
părinte
comite
df584eff1f
12 a modificat fișierele cu 1024 adăugiri și 707 ștergeri
  1. 0 1
      .eslintignore
  2. 13 2
      .eslintrc.js
  3. 22 7
      .postcssrc.js
  4. 13 2
      babel.config.js
  5. 12 4
      gulpfile.js
  6. 41 7
      package.json
  7. 1 1
      scripts/amo-upload.mjs
  8. 11 0
      scripts/common.js
  9. 0 59
      scripts/plaid.conf.js
  10. 239 0
      scripts/webpack-base.js
  11. 20 85
      scripts/webpack.conf.js
  12. 652 539
      yarn.lock

+ 0 - 1
.eslintignore

@@ -4,7 +4,6 @@
 !scripts/action-helper.js
 !scripts/fake-dep-loader.js
 !scripts/manifest-helper.js
-!scripts/plaid.conf.js
 !scripts/webpack.conf.js
 !scripts/webpack-protect-bootstrap-plugin.js
 !scripts/webpack-util.js

+ 13 - 2
.eslintrc.js

@@ -4,9 +4,20 @@ const ovr = makeOverrides();
 module.exports = {
   root: true,
   extends: [
-    require.resolve('@gera2ld/plaid/eslint'),
-    require.resolve('@gera2ld/plaid-common-vue/eslint/vue3-js'),
+    'eslint:recommended',
+    'plugin:vue/vue3-essential',
+    'prettier',
   ],
+  env: {
+    browser: true,
+    node: true,
+    es2021: true,
+  },
+  parserOptions: {
+    parser: '@babel/eslint-parser',
+    ecmaVersion: 'latest',
+    sourceType: 'module',
+  },
   plugins: ['jest'],
   rules: {
     'prettier/prettier': 'off',

+ 22 - 7
.postcssrc.js

@@ -1,8 +1,23 @@
-const { combineConfigSync } = require('@gera2ld/plaid');
-const base = require('@gera2ld/plaid/postcss/base');
+const { alias } = require('./scripts/common');
 
-module.exports = combineConfigSync({}, [base, (cfg) => {
-  cfg.parser = 'postcss-scss';
-  cfg.plugins.unshift('postcss-simple-vars');
-  return cfg;
-}]);
+module.exports = {
+  parser: 'postcss-scss',
+  plugins: [
+    'postcss-simple-vars',
+    // Transform @import, resolve `@` to `src`
+    require('postcss-import')({
+      resolve(id) {
+        if (id.startsWith('~')) {
+          const parts = id.slice(1).split('/');
+          parts[0] = alias[parts[0]] || parts[0];
+          return require.resolve(parts.join('/'));
+        }
+        return id;
+      },
+    }),
+    // Calculate at compile time
+    require('postcss-calc'),
+    require('postcss-nested'),
+    require('autoprefixer'),
+  ],
+};

+ 13 - 2
babel.config.js

@@ -1,8 +1,11 @@
+const { alias, extensions } = require('./scripts/common');
+
+const isTest = process.env.BABEL_ENV === 'test';
+
 module.exports = {
-  extends: require.resolve('@gera2ld/plaid/config/babelrc'),
   presets: [
     ['@babel/preset-env', {
-      ...process.env.BABEL_ENV !== 'test' && {
+      ...!isTest && {
         modules: false,
       },
       useBuiltIns: false,
@@ -12,6 +15,14 @@ module.exports = {
     }],
   ],
   plugins: [
+    ['@babel/plugin-transform-runtime', {
+      useESModules: !isTest,
+      version: '^7.5.0',
+    }],
+    ['babel-plugin-module-resolver', {
+      alias,
+      extensions,
+    }],
     './scripts/babel-plugin-safe-bind.js',
     ['@babel/plugin-transform-for-of', { assumeArray: true }],
     ['transform-modern-regexp', { useRe: true }],

+ 12 - 4
gulpfile.js

@@ -31,13 +31,21 @@ function watch() {
 }
 
 async function jsDev() {
-  require('@gera2ld/plaid-webpack/bin/develop')();
+  return runCommand('webpack-cli', ['-w', '--config', 'scripts/webpack.conf.js']);
 }
 
 async function jsProd() {
-  return require('@gera2ld/plaid-webpack/bin/build')({
-    api: true,
-    keep: true,
+  return runCommand('webpack-cli', ['--config', 'scripts/webpack.conf.js']);
+}
+
+function runCommand(command, args) {
+  return new Promise((resolve, reject) => {
+    const child = spawn(command, args, {
+      stdio: 'inherit',
+    });
+    child.on('close', (code, signal) => {
+      (code ? reject : resolve)(signal);
+    });
   });
 }
 

+ 41 - 7
package.json

@@ -14,7 +14,6 @@
     "lint": "run-s lint:js lint:yml",
     "lint:js": "eslint --ext .js,.vue . --cache",
     "lint:yml": "gulp check",
-    "svgo": "plaid svgo",
     "test": "cross-env BABEL_ENV=test TEST=test jest test",
     "ci": "run-p lint test",
     "bumpVersion": "gulp bump",
@@ -24,33 +23,69 @@
   "devDependencies": {
     "@actions/core": "^1.9.0",
     "@actions/github": "^5.0.3",
+    "@babel/core": "^7.22.10",
+    "@babel/eslint-parser": "^7.22.15",
     "@babel/helper-plugin-utils": "^7.18.6",
     "@babel/plugin-syntax-function-bind": "^7.18.6",
-    "@gera2ld/plaid": "~2.5.6",
-    "@gera2ld/plaid-common-ts": "~2.5.1",
-    "@gera2ld/plaid-test": "~2.5.8",
-    "@gera2ld/plaid-webpack": "~2.5.7",
-    "@gera2ld/plaid-webpack-vue3": "~2.5.7",
+    "@babel/plugin-transform-runtime": "^7.22.10",
+    "@babel/preset-env": "^7.22.10",
+    "@babel/preset-typescript": "^7.22.5",
     "@types/chrome": "^0.0.208",
     "@types/firefox-webext-browser": "94.0.1",
+    "@types/jest": "^29.5.3",
+    "@typescript-eslint/eslint-plugin": "^6.4.0",
+    "@typescript-eslint/parser": "^6.4.0",
     "@violentmonkey/types": "0.1.5",
+    "@vue/compiler-sfc": "^3.3.4",
+    "@vue/eslint-config-typescript": "^11.0.3",
     "amo-upload": "^0.5.2",
+    "autoprefixer": "^10.4.15",
+    "babel-jest": "^29.6.2",
+    "babel-loader": "^9.1.3",
+    "babel-plugin-module-resolver": "^5.0.0",
     "babel-plugin-transform-modern-regexp": "^0.0.6",
     "cross-env": "^7.0.3",
     "cross-spawn": "^7.0.3",
+    "css-loader": "^6.8.1",
+    "css-minimizer-webpack-plugin": "^5.0.1",
     "del": "^6.1.1",
+    "eslint": "^8.47.0",
+    "eslint-config-prettier": "^9.0.0",
+    "eslint-plugin-jest": "^27.2.3",
+    "eslint-plugin-prettier": "^5.0.0",
+    "eslint-plugin-vue": "^9.17.0",
     "fancy-log": "^2.0.0",
     "gulp": "^4.0.2",
     "gulp-plumber": "^1.1.0",
+    "html-webpack-plugin": "^5.5.3",
     "husky": "^8.0.1",
+    "jest": "^29.6.2",
     "jest-environment-jsdom": "^29.0.3",
     "js-yaml": "^4.1.0",
+    "lodash": "^4.17.21",
+    "mini-css-extract-plugin": "^2.7.6",
+    "npm-run-all": "^4.1.5",
     "plugin-error": "^2.0.0",
+    "postcss": "^8.4.28",
+    "postcss-calc": "^9.0.1",
+    "postcss-import": "^15.1.0",
+    "postcss-loader": "^7.3.3",
+    "postcss-nested": "^6.0.1",
     "postcss-scss": "^4.0.4",
     "postcss-simple-vars": "^6.0.3",
+    "prettier": "^3.0.2",
     "sharp": "^0.31.0",
+    "style-loader": "^3.3.3",
+    "svg-sprite-loader": "^6.0.11",
+    "terser-webpack-plugin": "^5.3.9",
     "through2": "^4.0.2",
+    "typescript": "^5.1.6",
     "vinyl": "^2.2.1",
+    "vue-loader": "^17.2.2",
+    "vue-tsc": "^1.8.8",
+    "webpack": "^5.88.2",
+    "webpack-bundle-analyzer": "^4.9.0",
+    "webpack-cli": "^5.1.4",
     "wrapper-webpack-plugin": "2.2.2"
   },
   "author": "Gerald <[email protected]>",
@@ -69,7 +104,6 @@
     "codemirror": "^5.65.10",
     "tldts": "^6.0.14",
     "vue": "^3.3.4",
-    "vue-loader": "^17.2.2",
     "vueleton": "^2.0.2"
   },
   "engines": {

+ 1 - 1
scripts/amo-upload.mjs

@@ -10,7 +10,7 @@ async function main() {
   const rawVersion = process.env.VERSION;
   const version = getVersion();
   const beta = isBeta();
-  const fileName = `violentmonkey-${version}${beta ? 'b' : ''}-an+fx.xpi`;
+  const fileName = `violentmonkey-${version}${beta ? 'b' : ''}.xpi`;
   const url = `https://github.com/violentmonkey/violentmonkey/releases/download/v${rawVersion}/${fileName}`;
 
   if (await hasAsset(fileName)) {

+ 11 - 0
scripts/common.js

@@ -0,0 +1,11 @@
+const path = require('path');
+
+const isProd = process.env.NODE_ENV === 'production';
+
+exports.isProd = isProd;
+exports.alias = {
+  '@': path.resolve('src'),
+};
+exports.extensions = [
+  '.ts', '.tsx', '.mjs', '.js', '.jsx', '.vue',
+];

+ 0 - 59
scripts/plaid.conf.js

@@ -1,59 +0,0 @@
-const { isProd } = require('@gera2ld/plaid/util');
-
-/**
- * For each entry, `key` is the chunk name, `value` has following properties:
- * - value.entry: webpack entry.
- * - value.html: options object passed to HtmlWebpackPlugin.
- * - value.html.inlineSource: if true, JS and CSS files will be inlined in HTML.
- */
-exports.pages = [
-  'background',
-  'confirm',
-  'options',
-  'popup',
-].reduce((res, name) => Object.assign(res, {
-  [`${name}/index`]: {
-    entry: `./src/${name}`,
-    html: name !== 'background' && (options => ({
-      ...options,
-      title: 'Violentmonkey',
-      injectTo: item => ((item.attributes.src || '').endsWith('/index.js') ? 'body' : 'head'),
-      scriptLoading: 'blocking', // we don't need `defer` and it breaks in some browsers, see #1632
-    })),
-  },
-}), {});
-
-const splitVendor = prefix => ({
-  [prefix]: {
-    test: new RegExp(`node_modules[/\\\\]${prefix}`),
-    name: `public/lib/${prefix}`,
-    chunks: 'all',
-    priority: 100,
-  },
-});
-
-exports.devServer = false;
-exports.devtool = isProd ? false : 'inline-source-map';
-exports.optimization = {
-  runtimeChunk: false,
-  splitChunks: {
-    cacheGroups: {
-      'common-ui': {
-        name: 'common-ui',
-        test: new RegExp([
-          /\bsvg/,
-          // don't extract CSS as it'll change the relative order of rules which breaks appearance
-          'src/common/(?!zip|.*\\.css$)',
-          'node_modules/@violentmonkey/shortcut',
-          'node_modules/@?vue',
-        ].map(re => re.source || re).join('|').replace(/\\?\//g, '[/\\\\]')),
-        chunks: c => ![
-          'background/index', // only 4kB of common code
-          'injected',
-          'injected-web',
-        ].includes(c.name),
-      },
-      ...splitVendor('codemirror'),
-    },
-  },
-};

+ 239 - 0
scripts/webpack-base.js

@@ -0,0 +1,239 @@
+const { resolve } = require('path');
+const { VueLoaderPlugin } = require('vue-loader');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
+const TerserPlugin = require('terser-webpack-plugin');
+const deepmerge = require('deepmerge');
+const { alias, extensions, isProd } = require('./common');
+
+const defaultHtmlOptions = {
+  minify: isProd && {
+    collapseWhitespace: true,
+    removeAttributeQuotes: true,
+    removeComments: true,
+    removeOptionalTags: true,
+    removeRedundantAttributes: true,
+    removeScriptTypeAttributes: true,
+    removeStyleLinkTypeAttributes: true,
+  },
+  meta: { viewport: 'width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0' },
+  css: [],
+  js: [],
+};
+const MIN_OPTS = {
+  extractComments: false,
+  parallel: true,
+  terserOptions: {
+    compress: {
+      // `terser` often inlines big one-time functions inside a small "hot" function
+      reduce_funcs: false,
+    },
+    output: {
+      ascii_only: true,
+      comments: false,
+      wrap_func_args: false, // disabling a premature optimization designed for old browsers
+    },
+  },
+};
+const MIN_OPTS_PUBLIC = isProd && {
+  include: 'public/',
+  ...MIN_OPTS,
+};
+const MIN_OPTS_MAIN = isProd && deepmerge.all([{}, MIN_OPTS, {
+  exclude: 'public/',
+  terserOptions: {
+    compress: {
+      ecma: 8, // ES2017 Object.entries and so on
+      passes: 2, // necessary now since we removed plaid's minimizer
+      unsafe_arrows: true, // it's 'safe' since we don't rely on function prototypes
+    },
+  },
+}]);
+const nodeModules = resolve('node_modules');
+
+const pages = [
+  'background',
+  'confirm',
+  'options',
+  'popup',
+];
+const createHtmlPage = key => new HtmlWebpackPlugin({
+  ...defaultHtmlOptions,
+  filename: `${key}/index.html`,
+  chunks: [`${key}/index`],
+  title: 'Violentmonkey',
+  inject: 'body',
+  scriptLoading: 'blocking', // we don't need `defer` and it breaks in some browsers, see #1632
+});
+
+const splitVendor = prefix => ({
+  [prefix]: {
+    test: new RegExp(`node_modules[/\\\\]${prefix}`),
+    name: `public/lib/${prefix}`,
+    chunks: 'all',
+    priority: 100,
+  },
+});
+
+function styleLoader(options) {
+  const {
+    extract,
+    loaders = [],
+    fallback = 'style-loader',
+    modules = false,
+  } = options || {};
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      modules,
+      importLoaders: 1,
+      sourceMap: false,
+    },
+  };
+  return [
+    extract ? MiniCssExtractPlugin.loader : fallback,
+    cssLoader,
+    ...loaders,
+  ];
+}
+
+function styleRule(options, rule) {
+  return {
+    test: /\.css$/,
+    use: styleLoader(options),
+    ...rule,
+  };
+}
+
+const styleOptions = {
+  extract: isProd,
+};
+const postcssLoader = {
+  loader: 'postcss-loader',
+};
+
+const getBaseConfig = () => ({
+  mode: isProd ? 'production' : 'development',
+  target: 'web', // required by live reloading
+  devtool: isProd ? false : 'inline-source-map',
+  output: {
+    path: resolve('dist'),
+    publicPath: '/',
+    filename: '[name].js',
+    hashFunction: 'xxhash64',
+  },
+  node: {
+    global: false,
+  },
+  performance: {
+    maxEntrypointSize: 1e6,
+    maxAssetSize: 0.5e6,
+  },
+  resolve: {
+    alias,
+    extensions,
+  },
+  module: {
+    rules: [
+      // JS/TS
+      {
+        test: /\.m?[jt]sx?$/,
+        use: 'babel-loader',
+        exclude: file => /node_modules/.test(file) && !/vueleton|@vue[/\\]shared/.test(file),
+      },
+      // CSS
+      {
+        oneOf: [
+          // library CSS files: node_modules/**/*.css
+          styleRule(styleOptions, {
+            include: [nodeModules],
+          }),
+          // CSS modules: src/**/*.module.css
+          styleRule({
+            ...styleOptions,
+            loaders: [postcssLoader],
+            modules: {},
+          }, {
+            test: /\.module\.css$/,
+          }),
+          // normal CSS files: src/**/*.css
+          styleRule({
+            ...styleOptions,
+            loaders: [postcssLoader],
+          }),
+        ],
+      },
+      // SVG
+      {
+        test: /\.svg$/,
+        use: [{
+          loader: 'svg-sprite-loader',
+          options: {
+            // extract: extractSVG,
+          },
+        }],
+        include: [resolve('src/resources/svg')],
+      },
+      // Vue
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: {
+          babelParserPlugins: ['functionBind'],
+          compilerOptions: {
+            whitespace: 'condense',
+          },
+        },
+      },
+    ],
+  },
+  optimization: {
+    runtimeChunk: false,
+    splitChunks: {
+      cacheGroups: {
+        'common-ui': {
+          name: 'common-ui',
+          test: new RegExp([
+            /\bsvg/,
+            // don't extract CSS as it'll change the relative order of rules which breaks appearance
+            'src/common/(?!zip|.*\\.css$)',
+            'node_modules/@violentmonkey/shortcut',
+            'node_modules/@?vue',
+          ].map(re => re.source || re).join('|').replace(/\\?\//g, '[/\\\\]')),
+          chunks: c => ![
+            'background/index', // only 4kB of common code
+            'injected',
+            'injected-web',
+          ].includes(c.name),
+        },
+        ...splitVendor('codemirror'),
+      },
+    },
+    minimizer: isProd ? [
+      new CssMinimizerPlugin(),
+      new TerserPlugin(MIN_OPTS_PUBLIC),
+      new TerserPlugin(MIN_OPTS_MAIN),
+    ] : [],
+  },
+  plugins: [
+    new VueLoaderPlugin(),
+    ...styleOptions.extract ? [new MiniCssExtractPlugin({
+      filename: '[name].css',
+    })] : [],
+  ],
+});
+
+const getPageConfig = () => {
+  const config = getBaseConfig();
+  config.entry = Object.fromEntries(pages.map(name => [`${name}/index`, `./src/${name}`]));
+  config.plugins = [
+    ...config.plugins,
+    ...pages.filter(key => key !== 'background').map(createHtmlPage),
+  ];
+  return config;
+};
+
+exports.isProd = isProd;
+exports.getBaseConfig = getBaseConfig;
+exports.getPageConfig = getPageConfig;

+ 20 - 85
scripts/webpack.conf.js

@@ -1,59 +1,16 @@
-const { modifyWebpackConfig, shallowMerge, defaultOptions } = require('@gera2ld/plaid');
-const { isProd } = require('@gera2ld/plaid/util');
 const webpack = require('webpack');
-const TerserPlugin = isProd && require('terser-webpack-plugin');
-const deepmerge = isProd && require('deepmerge');
 const { ListBackgroundScriptsPlugin } = require('./manifest-helper');
 const { addWrapperWithGlobals, getCodeMirrorThemes } = require('./webpack-util');
 const ProtectWebpackBootstrapPlugin = require('./webpack-protect-bootstrap-plugin');
-const projectConfig = require('./plaid.conf');
 const { getVersion } = require('./version-helper');
 const { configLoader } = require('./config-helper');
-const mergedConfig = shallowMerge(defaultOptions, projectConfig);
+const { getBaseConfig, getPageConfig, isProd } = require('./webpack-base');
 
 // Avoiding collisions with globals of a content-mode userscript
 const INIT_FUNC_NAME = '**VMInitInjection**';
 const VAULT_ID = 'VAULT_ID';
 const PAGE_MODE_HANDSHAKE = 'PAGE_MODE_HANDSHAKE';
 const VM_VER = getVersion();
-const WEBPACK_OPTS = {
-  node: {
-    global: false,
-  },
-  performance: {
-    maxEntrypointSize: 1e6,
-    maxAssetSize: 0.5e6,
-  },
-};
-const MIN_OPTS = {
-  extractComments: false,
-  parallel: true,
-  terserOptions: {
-    compress: {
-      // `terser` often inlines big one-time functions inside a small "hot" function
-      reduce_funcs: false,
-    },
-    output: {
-      ascii_only: true,
-      comments: false,
-      wrap_func_args: false, // disabling a premature optimization designed for old browsers
-    },
-  },
-};
-const MIN_OPTS_PUBLIC = isProd && {
-  include: 'public/',
-  ...MIN_OPTS,
-};
-const MIN_OPTS_MAIN = isProd && deepmerge.all([{}, MIN_OPTS, {
-  exclude: 'public/',
-  terserOptions: {
-    compress: {
-      ecma: 8, // ES2017 Object.entries and so on
-      passes: 2, // necessary now since we removed plaid's minimizer
-      unsafe_arrows: true, // it's 'safe' since we don't rely on function prototypes
-    },
-  },
-}]);
 
 configLoader
   // Default values
@@ -98,45 +55,23 @@ const skipReinjectionHeader = `{
   const INIT_FUNC_NAME = '${INIT_FUNC_NAME}';
   if (window[INIT_FUNC_NAME] !== 1)`;
 
-const modify = (page, entry, init) => modifyWebpackConfig(
-  (config) => {
-    Object.assign(config, WEBPACK_OPTS);
-    config.output.publicPath = '/';
-    config.plugins.push(new webpack.DefinePlugin({
-      ...defsObj,
-      // Conditional compilation to remove unsafe and unused stuff from `injected`
-      'process.env.IS_INJECTED': JSON.stringify(/injected/.test(page) && page),
-    }));
-    config.optimization.minimizer.find((m, i, arr) => (
-      m.constructor.name === 'TerserPlugin' && arr.splice(i, 1)
-    ));
-    config.optimization.minimizer.push(...!isProd ? [] : [
-      new TerserPlugin(MIN_OPTS_PUBLIC),
-      new TerserPlugin(MIN_OPTS_MAIN),
-    ]);
-    config.module.rules.find(rule => {
-      if (typeof rule.test?.test === 'function' && rule.test.test('file.js')) {
-        rule.exclude = file => /node_modules/.test(file) && !/vueleton|@vue[/\\]shared/.test(file);
-      }
-      const vueOpts = rule.test?.source.includes('.vue') && rule.options;
-      if (vueOpts) {
-        const arr = vueOpts.babelParserPlugins || (vueOpts.babelParserPlugins = []);
-        if (!arr.includes('functionBind')) arr.push('functionBind');
-      }
-    });
-    if (!entry) init = page;
-    if (init) init(config);
-    return config;
-  }, {
-    projectConfig: {
-      ...mergedConfig,
-      ...entry && { pages: { [page]: { entry } } },
-    },
-  },
-);
+const buildConfig = (page, entry, init) => {
+  const config = entry ? getBaseConfig() : getPageConfig();
+  config.plugins.push(new webpack.DefinePlugin({
+    ...defsObj,
+    // Conditional compilation to remove unsafe and unused stuff from `injected`
+    'process.env.IS_INJECTED': JSON.stringify(/injected/.test(page) && page),
+  }));
+  if (typeof entry === 'string') {
+    config.entry = { [page]: entry };
+  }
+  if (!entry) init = page;
+  if (init) init(config);
+  return config;
+};
 
-module.exports = Promise.all([
-  modify((config) => {
+module.exports = [
+  buildConfig((config) => {
     addWrapperWithGlobals('common', config, defsObj, getGlobals => ({
       header: () => `{ ${getGlobals()}`,
       footer: '}',
@@ -147,7 +82,7 @@ module.exports = Promise.all([
     }));
   }),
 
-  modify('injected', './src/injected', (config) => {
+  buildConfig('injected', './src/injected', (config) => {
     config.plugins.push(new ProtectWebpackBootstrapPlugin());
     addWrapperWithGlobals('injected/content', config, defsObj, getGlobals => ({
       header: () => `${skipReinjectionHeader} { ${getGlobals()}`,
@@ -155,7 +90,7 @@ module.exports = Promise.all([
     }));
   }),
 
-  modify('injected-web', './src/injected/web', (config) => {
+  buildConfig('injected-web', './src/injected/web', (config) => {
     config.output.libraryTarget = 'commonjs2';
     config.plugins.push(new ProtectWebpackBootstrapPlugin());
     addWrapperWithGlobals('injected/web', config, defsObj, getGlobals => ({
@@ -169,4 +104,4 @@ module.exports = Promise.all([
         }};0;`,
     }));
   }),
-]);
+];

Fișier diff suprimat deoarece este prea mare
+ 652 - 539
yarn.lock


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff