feat(schematics): switch to angular/cli 1.7
This commit is contained in:
parent
b97305822a
commit
acdeb1b71c
@ -13,7 +13,7 @@ describe('Nrwl Workspace', () => {
|
||||
`
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { MyLibModule } from '@nrwl/my-dir/my-lib';
|
||||
import { MyLibModule } from '@proj/my-dir/my-lib';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
|
||||
@ -17,15 +17,15 @@ describe('Command line', () => {
|
||||
'apps/myapp/src/main.ts',
|
||||
`
|
||||
import '../../../libs/mylib';
|
||||
import '@nrwl/lazylib';
|
||||
import '@nrwl/mylib/deep';
|
||||
import '@nrwl/myapp';
|
||||
import '@nrwl/myapp/main';
|
||||
import '@proj/lazylib';
|
||||
import '@proj/mylib/deep';
|
||||
import '@proj/myapp';
|
||||
import '@proj/myapp/main';
|
||||
`
|
||||
);
|
||||
|
||||
const out = runCLI('lint --type-check', { silenceError: true });
|
||||
expect(out).toContain('library imports must start with @nrwl/');
|
||||
expect(out).toContain('library imports must start with @proj/');
|
||||
expect(out).toContain('imports of lazy-loaded libraries are forbidden');
|
||||
expect(out).toContain('deep imports into libraries are forbidden');
|
||||
expect(out).toContain('imports of apps are forbidden');
|
||||
@ -94,7 +94,7 @@ describe('Command line', () => {
|
||||
newApp('myapp2');
|
||||
newLib('mylib');
|
||||
|
||||
updateFile('apps/myapp/src/app/app.component.spec.ts', `import '@nrwl/mylib';`);
|
||||
updateFile('apps/myapp/src/app/app.component.spec.ts', `import '@proj/mylib';`);
|
||||
|
||||
const affectedApps = runCommand('npm run affected:apps -- --files="libs/mylib/index.ts"');
|
||||
expect(affectedApps).toContain('myapp');
|
||||
|
||||
@ -51,7 +51,6 @@ describe('Nrwl Convert to Nx Workspace', () => {
|
||||
const updatedPackageJson = JSON.parse(readFile('package.json'));
|
||||
expect(updatedPackageJson.description).toEqual('some description');
|
||||
expect(updatedPackageJson.devDependencies['@nrwl/schematics']).toBeDefined();
|
||||
expect(updatedPackageJson.dependencies['@angular/cli']).toEqual('file:.angular_cli165.tgz');
|
||||
expect(updatedPackageJson.dependencies['@nrwl/nx']).toBeDefined();
|
||||
expect(updatedPackageJson.dependencies['@ngrx/store']).toBeDefined();
|
||||
expect(updatedPackageJson.dependencies['@ngrx/effects']).toBeDefined();
|
||||
@ -76,8 +75,6 @@ describe('Nrwl Convert to Nx Workspace', () => {
|
||||
a: ['b'],
|
||||
'@proj/*': ['libs/*']
|
||||
});
|
||||
|
||||
expect(fileExists('./tmp/proj/.angular_cli165.tgz')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should generate a workspace and not change dependencies or devDependencies if they already exist', () => {
|
||||
|
||||
@ -13,7 +13,8 @@ export function runNgNew(command?: string): string {
|
||||
export function newProject(): void {
|
||||
cleanup();
|
||||
if (!directoryExists('./tmp/proj_backup')) {
|
||||
runNgNew('--collection=@nrwl/schematics --npmScope=nrwl');
|
||||
runNgNew('--collection=@nrwl/schematics --npmScope=proj');
|
||||
execSync('npm i', {cwd: `./tmp/${projectName}`});
|
||||
copyMissingPackages();
|
||||
execSync('mv ./tmp/proj ./tmp/proj_backup');
|
||||
}
|
||||
|
||||
35
package.json
35
package.json
@ -23,27 +23,26 @@
|
||||
"jasmine-marbles": "0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "file:./packages/schematics/src/collection/application/files/__directory__/.angular_cli165.tgz",
|
||||
"@angular-devkit/core": "0.0.29",
|
||||
"@angular-devkit/schematics": "0.0.52",
|
||||
"@schematics/angular": "0.1.17",
|
||||
"@angular/common": "5.2.1",
|
||||
"@angular/compiler": "5.2.1",
|
||||
"@angular/compiler-cli": "5.2.1",
|
||||
"@angular/core": "5.2.1",
|
||||
"@angular/platform-browser": "5.2.1",
|
||||
"@angular/platform-browser-dynamic": "5.2.1",
|
||||
"@angular/router": "5.2.1",
|
||||
"@angular/upgrade": "5.2.1",
|
||||
"@ngrx/effects": "4.1.1",
|
||||
"@ngrx/router-store": "4.1.1",
|
||||
"@ngrx/store": "4.1.1",
|
||||
"@ngrx/store-devtools": "4.0.0",
|
||||
"@types/jasmine": "2.5.53",
|
||||
"@types/node": "8.0.7",
|
||||
"@angular/cli": "1.7.1",
|
||||
"@angular/common": "5.2.6",
|
||||
"@angular/compiler": "5.2.6",
|
||||
"@angular/compiler-cli": "5.2.6",
|
||||
"@angular/core": "5.2.6",
|
||||
"@angular/platform-browser": "5.2.6",
|
||||
"@angular/platform-browser-dynamic": "5.2.6",
|
||||
"@angular/router": "5.2.6",
|
||||
"@angular/upgrade": "5.2.6",
|
||||
"@ngrx/effects": "5.1.0",
|
||||
"@ngrx/router-store": "5.0.1",
|
||||
"@ngrx/store": "5.1.0",
|
||||
"@ngrx/store-devtools": "5.1.0",
|
||||
"@types/jasmine": "~2.8.3",
|
||||
"@types/jasminewd2": "~2.0.2",
|
||||
"@types/node": "~6.0.60",
|
||||
"angular": "1.6.6",
|
||||
"husky": "^0.14.3",
|
||||
"jasmine-core": "~2.8.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jest": "20.0.4",
|
||||
"karma": "~2.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
|
||||
@ -39,34 +39,12 @@ writeFileSync(
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'@nrwl/schematics': nxVersion,
|
||||
'@angular/cli': 'file:.angular_cli165.tgz',
|
||||
'@angular-devkit/core': '^0.0.29',
|
||||
'@angular-devkit/schematics': '0.0.52',
|
||||
'@schematics/angular': '0.1.17'
|
||||
'@angular/cli': '1.7.1'
|
||||
},
|
||||
license: 'MIT'
|
||||
})
|
||||
);
|
||||
|
||||
copyFile(
|
||||
path.join(
|
||||
path.dirname(__dirname),
|
||||
'src',
|
||||
'collection',
|
||||
'application',
|
||||
'files',
|
||||
'__directory__',
|
||||
'.angular_cli165.tgz'
|
||||
),
|
||||
tmpDir
|
||||
);
|
||||
|
||||
function copyFile(file: string, target: string) {
|
||||
const f = path.basename(file);
|
||||
const source = readFileSync(file);
|
||||
writeFileSync(path.join(target, f), source);
|
||||
}
|
||||
|
||||
if (useYarn) {
|
||||
execSync('yarn install --silent', { cwd: tmpDir, stdio: [0, 1, 2] });
|
||||
} else {
|
||||
|
||||
40
packages/schematics/migrations/20180225-switch-to-cli17.ts
Normal file
40
packages/schematics/migrations/20180225-switch-to-cli17.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { updateJsonFile } from '../src/collection/utility/fileutils';
|
||||
import { unlinkSync } from 'fs';
|
||||
|
||||
export default {
|
||||
description: 'Switch to Angular CLI 1.7',
|
||||
run: () => {
|
||||
updateJsonFile('package.json', json => {
|
||||
json.devDependencies = {
|
||||
...json.devDependencies,
|
||||
"@angular/cli": "1.7.1",
|
||||
"@angular/compiler-cli": "5.2.6",
|
||||
"@angular/language-service": "5.2.6",
|
||||
"@types/jasmine": "~2.5.53",
|
||||
['@angular-devkit/core']: undefined,
|
||||
['@angular-devkit/schematics']: undefined,
|
||||
['@schematics/angular']: undefined,
|
||||
['karma-cli']: undefined
|
||||
};
|
||||
|
||||
|
||||
json.dependencies = {
|
||||
...json.dependencies,
|
||||
"@angular/animations": "5.2.6",
|
||||
"@angular/common": "5.2.6",
|
||||
"@angular/compiler": "5.2.6",
|
||||
"@angular/core": "5.2.6",
|
||||
"@angular/forms": "5.2.6",
|
||||
"@angular/platform-browser": "5.2.6",
|
||||
"@angular/platform-browser-dynamic": "5.2.6",
|
||||
"@angular/router": "5.2.6",
|
||||
"@ngrx/effects": "5.1.0",
|
||||
"@ngrx/router-store": "5.0.1",
|
||||
"@ngrx/store": "5.1.0",
|
||||
"@ngrx/store-devtools": "5.1.0"
|
||||
};
|
||||
});
|
||||
|
||||
unlinkSync('.angular_cli165.tgz');
|
||||
}
|
||||
};
|
||||
@ -14,7 +14,7 @@ import {
|
||||
url
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import * as stringUtils from '@schematics/angular/strings';
|
||||
import {strings} from '@angular-devkit/core';
|
||||
import { addImportToModule, insert, toFileName } from '@nrwl/schematics';
|
||||
import * as ts from 'typescript';
|
||||
import { addBootstrapToModule } from '@schematics/angular/utility/ast-utils';
|
||||
@ -162,7 +162,7 @@ export default function(schema: Schema): Rule {
|
||||
const options = normalizeOptions(schema);
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
utils: stringUtils,
|
||||
utils: strings,
|
||||
dot: '.',
|
||||
tmpl: '',
|
||||
offsetFromRoot: offsetFromRoot(options.fullPath),
|
||||
@ -205,6 +205,6 @@ export default function(schema: Schema): Rule {
|
||||
function normalizeOptions(options: Schema): NormalizedSchema {
|
||||
const name = toFileName(options.name);
|
||||
const fullName = options.directory ? `${toFileName(options.directory)}/${name}` : name;
|
||||
const fullPath = `apps/${fullName}/${options.sourceDir}`;
|
||||
return { ...options, name, fullName, fullPath };
|
||||
const fullPath = `apps/${fullName}/src`;
|
||||
return { ...options, sourceDir: 'src', name, fullName, fullPath };
|
||||
}
|
||||
|
||||
@ -16,11 +16,6 @@
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed"
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string",
|
||||
"default": "src",
|
||||
"alias": "sd"
|
||||
},
|
||||
"inlineStyle": {
|
||||
"description": "Specifies if the style will be in the ts file.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -16,7 +16,6 @@ describe('application', () => {
|
||||
it('should generate files', () => {
|
||||
const tree = schematicRunner.runSchematic('application', { name: 'myApp', directory: 'my-app' }, appTree);
|
||||
expect(tree.files).toEqual([
|
||||
'/my-app/.angular_cli165.tgz',
|
||||
'/my-app/README.md',
|
||||
'/my-app/.angular-cli.json',
|
||||
'/my-app/.editorconfig',
|
||||
|
||||
Binary file not shown.
@ -21,11 +21,16 @@
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"name": "$workspaceRoot",
|
||||
"root": ".",
|
||||
"appRoot": ""
|
||||
}
|
||||
],
|
||||
"defaults": {
|
||||
"schematics": {
|
||||
"collection": "@nrwl/schematics",
|
||||
"postGenerate": "npm run format",
|
||||
"newProject": ["app", "lib"]
|
||||
"collection": "@nrwl/schematics"
|
||||
},
|
||||
"styleExt": "<%= style %>",<% if (!minimal) { %>
|
||||
"component": {}<% } else { %>
|
||||
|
||||
@ -43,19 +43,16 @@
|
||||
"zone.js": "^0.8.19",
|
||||
"@nrwl/nx": "<%= nxVersion %>",
|
||||
"@ngrx/effects": "<%= ngrxVersion %>",
|
||||
"@ngrx/router-store": "<%= ngrxVersion %>",
|
||||
"@ngrx/router-store": "<%= routerStoreVersion %>",
|
||||
"@ngrx/store": "<%= ngrxVersion %>",
|
||||
"@ngrx/store-devtools": "4.0.0"
|
||||
"@ngrx/store-devtools": "<%= ngrxVersion %>"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "<%= angularCliVersion %>",
|
||||
"@angular/compiler-cli": "<%= angularVersion %>",
|
||||
"@angular-devkit/core": "<%= devKitCoreVersion %>",
|
||||
"@angular-devkit/schematics": "<%= devKitSchematicsVersion %>",
|
||||
"@schematics/angular": "<%= schematicsAngularVersion %>",
|
||||
"@nrwl/schematics": "<%= schematicsVersion %>",
|
||||
"@angular/language-service": "<%= angularVersion %>",<% if (!minimal) { %>
|
||||
"@types/jasmine": "~2.5.53",
|
||||
"@types/jasmine": "~2.8.3",
|
||||
"@types/jasminewd2": "~2.0.2",
|
||||
"@types/node": "~6.0.60",
|
||||
"codelyzer": "^4.0.1",
|
||||
@ -63,7 +60,6 @@
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~2.0.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-cli": "~1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "^1.2.1",
|
||||
"karma-jasmine": "~1.1.0",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { apply, branchAndMerge, chain, mergeWith, move, Rule, template, Tree, url } from '@angular-devkit/schematics';
|
||||
import { Schema } from './schema';
|
||||
import * as stringUtils from '@schematics/angular/strings';
|
||||
import {strings} from '@angular-devkit/core';
|
||||
import { libVersions } from '../utility/lib-versions';
|
||||
|
||||
export default function(options: Schema): Rule {
|
||||
const npmScope = options.npmScope ? options.npmScope : options.name;
|
||||
const templateSource = apply(url('./files'), [
|
||||
template({
|
||||
utils: stringUtils,
|
||||
utils: strings,
|
||||
dot: '.',
|
||||
...libVersions,
|
||||
...(options as object),
|
||||
|
||||
@ -206,6 +206,6 @@ export default function(schema: Schema): Rule {
|
||||
function normalizeOptions(options: Schema): NormalizedSchema {
|
||||
const name = toFileName(options.name);
|
||||
const fullName = options.directory ? `${toFileName(options.directory)}/${name}` : name;
|
||||
const fullPath = `libs/${fullName}/${options.sourceDir}`;
|
||||
return { ...options, name, fullName, fullPath };
|
||||
const fullPath = `libs/${fullName}/src`;
|
||||
return { ...options, sourceDir: 'src', name, fullName, fullPath };
|
||||
}
|
||||
|
||||
@ -12,11 +12,6 @@
|
||||
"type": "string",
|
||||
"description": "A directory where the app is placed"
|
||||
},
|
||||
"sourceDir": {
|
||||
"type": "string",
|
||||
"default": "src",
|
||||
"alias": "sd"
|
||||
},
|
||||
"nomodule": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
|
||||
@ -18,7 +18,7 @@ import { addImportToModule, addProviderToModule, insert, offset } from '../utili
|
||||
import { insertImport } from '@schematics/angular/utility/route-utils';
|
||||
import { Schema } from './schema';
|
||||
import { InsertChange } from '@schematics/angular/utility/change';
|
||||
import { ngrxVersion } from '../utility/lib-versions';
|
||||
import {ngrxVersion, routerStoreVersion} from '../utility/lib-versions';
|
||||
import { serializeJson } from '../utility/fileutils';
|
||||
|
||||
function addImportsToModule(name: string, options: Schema): Rule {
|
||||
@ -115,7 +115,7 @@ function addNgRxToPackageJson() {
|
||||
json['dependencies']['@ngrx/store'] = ngrxVersion;
|
||||
}
|
||||
if (!json['dependencies']['@ngrx/router-store']) {
|
||||
json['dependencies']['@ngrx/router-store'] = ngrxVersion;
|
||||
json['dependencies']['@ngrx/router-store'] = routerStoreVersion;
|
||||
}
|
||||
if (!json['dependencies']['@ngrx/effects']) {
|
||||
json['dependencies']['@ngrx/effects'] = ngrxVersion;
|
||||
|
||||
@ -18,6 +18,14 @@ describe('fileutils', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should put workspaceRoot last', () => {
|
||||
expect(addApp([{ name: 'a' }, { name: 'z' }], { name: '$workspaceRoot' })).toEqual([
|
||||
{ name: 'a' },
|
||||
{ name: 'z' },
|
||||
{ name: '$workspaceRoot' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should prioritize apps with "main" defined', () => {
|
||||
expect(
|
||||
addApp([{ name: 'c' }, { name: 'a' }, { name: 'a', main: 'a' }], {
|
||||
|
||||
@ -15,6 +15,8 @@ export function addApp(apps: any[] | undefined, newApp: any): any[] {
|
||||
apps.push(newApp);
|
||||
|
||||
apps.sort((a: any, b: any) => {
|
||||
if (a.name === '$workspaceRoot') return 1;
|
||||
if (b.name === '$workspaceRoot') return -1;
|
||||
if (a.main && !b.main) return -1;
|
||||
if (!a.main && b.main) return 1;
|
||||
if (a.name > b.name) return 1;
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
export const angularCliVersion = 'file:.angular_cli165.tgz';
|
||||
export const angularVersion = '^5.2.0';
|
||||
export const angularCliVersion = '1.7.1';
|
||||
export const angularVersion = '5.2.6';
|
||||
export const angularJsVersion = '1.6.6';
|
||||
export const ngrxVersion = '4.1.1';
|
||||
export const ngrxVersion = '5.1.0';
|
||||
export const routerStoreVersion = '5.0.1';
|
||||
export const nxVersion = '*';
|
||||
export const schematicsVersion = '*';
|
||||
export const angularCliSchema = './node_modules/@nrwl/schematics/src/schema.json';
|
||||
export const latestMigration = '20180130-angular-devkit-schematics';
|
||||
export const latestMigration = '20180225-switch-to-cli17';
|
||||
export const prettierVersion = '1.10.2';
|
||||
export const typescriptVersion = '2.6.2';
|
||||
export const rxjsVersion = '^5.5.6';
|
||||
@ -27,5 +28,6 @@ export const libVersions = {
|
||||
rxjsVersion,
|
||||
devKitCoreVersion,
|
||||
devKitSchematicsVersion,
|
||||
schematicsAngularVersion
|
||||
schematicsAngularVersion,
|
||||
routerStoreVersion
|
||||
};
|
||||
|
||||
@ -5,14 +5,10 @@ import { join } from 'path';
|
||||
import {
|
||||
angularCliSchema,
|
||||
angularCliVersion,
|
||||
devKitCoreVersion,
|
||||
devKitSchematicsVersion,
|
||||
latestMigration,
|
||||
ngrxVersion,
|
||||
nxVersion,
|
||||
prettierVersion,
|
||||
schematicsAngularVersion,
|
||||
schematicsVersion
|
||||
prettierVersion, routerStoreVersion, schematicsVersion,
|
||||
} from '../utility/lib-versions';
|
||||
import * as fs from 'fs';
|
||||
import { copyFile, serializeJson, updateJsonFile } from '../utility/fileutils';
|
||||
@ -40,7 +36,7 @@ function updatePackageJson() {
|
||||
packageJson.dependencies['@ngrx/store'] = ngrxVersion;
|
||||
}
|
||||
if (!packageJson.dependencies['@ngrx/router-store']) {
|
||||
packageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
|
||||
packageJson.dependencies['@ngrx/router-store'] = routerStoreVersion;
|
||||
}
|
||||
if (!packageJson.dependencies['@ngrx/effects']) {
|
||||
packageJson.dependencies['@ngrx/effects'] = ngrxVersion;
|
||||
@ -57,15 +53,6 @@ function updatePackageJson() {
|
||||
if (!packageJson.devDependencies['prettier']) {
|
||||
packageJson.devDependencies['prettier'] = prettierVersion;
|
||||
}
|
||||
if (!packageJson.devDependencies['@angular-devkit/core']) {
|
||||
packageJson.devDependencies['@angular-devkit/core'] = devKitCoreVersion;
|
||||
}
|
||||
if (!packageJson.devDependencies['@angular-devkit/schematics']) {
|
||||
packageJson.devDependencies['@angular-devkit/schematics'] = devKitSchematicsVersion;
|
||||
}
|
||||
if (!packageJson.devDependencies['@schematics/angular']) {
|
||||
packageJson.devDependencies['@schematics/angular'] = schematicsAngularVersion;
|
||||
}
|
||||
|
||||
packageJson.scripts['apps:affected'] = './node_modules/.bin/nx affected apps';
|
||||
packageJson.scripts['build:affected'] = './node_modules/.bin/nx affected build';
|
||||
@ -251,13 +238,6 @@ function moveFiles(options: Schema) {
|
||||
};
|
||||
}
|
||||
|
||||
function copyAngularCliTgz() {
|
||||
return (host: Tree) => {
|
||||
copyFile(path.join(__dirname, '..', 'application', 'files', '__directory__', '.angular_cli165.tgz'), '.');
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
function dedup(array: any[]): any[] {
|
||||
const res = [];
|
||||
|
||||
@ -298,7 +278,6 @@ export default function(schema: Schema): Rule {
|
||||
updateAngularCLIJson(options),
|
||||
updateTsConfigsJson(options),
|
||||
updateProtractorConf(),
|
||||
updateTsLintJson(options),
|
||||
copyAngularCliTgz()
|
||||
updateTsLintJson(options)
|
||||
]);
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ function getFilesFromShash(sha1: string, sha2: string): string[] {
|
||||
|
||||
export function getAffectedApps(touchedFiles: string[]): string[] {
|
||||
const config = JSON.parse(fs.readFileSync('.angular-cli.json', 'utf-8'));
|
||||
const projects = (config.apps ? config.apps : []).map(p => {
|
||||
const projects = (config.apps ? config.apps : []).filter(p => p.name !== '$workspaceRoot').map(p => {
|
||||
return {
|
||||
name: p.name,
|
||||
isApp: p.root.startsWith('apps/'),
|
||||
@ -60,7 +60,7 @@ export function getAffectedApps(touchedFiles: string[]): string[] {
|
||||
|
||||
export function getAppRoots(appNames: string[]): string[] {
|
||||
const config = JSON.parse(fs.readFileSync('.angular-cli.json', 'utf-8'));
|
||||
return (config.apps ? config.apps : []).filter(p => appNames.indexOf(p.name) > -1).map(p => path.dirname(p.root));
|
||||
return (config.apps ? config.apps : []).filter(p => p.name !== '$workspaceRoot').filter(p => appNames.indexOf(p.name) > -1).map(p => path.dirname(p.root));
|
||||
}
|
||||
|
||||
function allFilesInDir(dirName: string): string[] {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user