feat(vue): update generated file setup for apps (#19428)
This commit is contained in:
parent
dd50756d8f
commit
601b74d50e
@ -177,20 +177,7 @@ exports[`application generator should set up project correctly with given option
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`application generator should set up project correctly with given options 5`] = `
|
exports[`application generator should set up project correctly with given options 5`] = `null`;
|
||||||
"import { describe, it, expect } from 'vitest';
|
|
||||||
|
|
||||||
import { mount } from '@vue/test-utils';
|
|
||||||
import App from '../App.vue';
|
|
||||||
|
|
||||||
describe('App', () => {
|
|
||||||
it('renders properly', () => {
|
|
||||||
const wrapper = mount(App, {});
|
|
||||||
expect(wrapper.text()).toContain('Welcome test 👋');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`application generator should set up project correctly with given options 6`] = `
|
exports[`application generator should set up project correctly with given options 6`] = `
|
||||||
[
|
[
|
||||||
@ -212,9 +199,9 @@ exports[`application generator should set up project correctly with given option
|
|||||||
"test/.eslintrc.json",
|
"test/.eslintrc.json",
|
||||||
"test/index.html",
|
"test/index.html",
|
||||||
"test/project.json",
|
"test/project.json",
|
||||||
"test/src/__tests__/App.spec.ts",
|
"test/src/app/App.spec.ts",
|
||||||
"test/src/App.vue",
|
"test/src/app/App.vue",
|
||||||
"test/src/components/NxWelcome.vue",
|
"test/src/app/NxWelcome.vue",
|
||||||
"test/src/main.ts",
|
"test/src/main.ts",
|
||||||
"test/src/styles.css",
|
"test/src/styles.css",
|
||||||
"test/tsconfig.app.json",
|
"test/tsconfig.app.json",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect } from 'vitest'
|
||||||
<% } %>
|
<% } %>
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import App from '../App.vue';
|
import App from './App.vue';
|
||||||
|
|
||||||
describe('App', () => {
|
describe('App', () => {
|
||||||
it('renders properly', () => {
|
it('renders properly', () => {
|
||||||
@ -1,8 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
<% if (routing) { %>
|
<% if (routing) { %>
|
||||||
import { RouterLink, RouterView } from 'vue-router';
|
import { RouterLink, RouterView } from 'vue-router';
|
||||||
|
<% } else { %>
|
||||||
|
import NxWelcome from './NxWelcome.vue';
|
||||||
<% } %>
|
<% } %>
|
||||||
import NxWelcome from './components/NxWelcome.vue';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -6,7 +6,7 @@ import router from './router';
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './app/App.vue';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
<% if (routing) { %>
|
<% if (routing) { %>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NxWelcome from '../components/NxWelcome.vue'
|
import NxWelcome from '../app/NxWelcome.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@ -11,22 +11,16 @@ defineProps<{}>();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Hello!</p>
|
||||||
<p>Welcome to Hello!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`component should generate files with jest 2`] = `
|
exports[`component should generate files with jest 2`] = `
|
||||||
"import { mount } from '@vue/test-utils';
|
"import { mount } from '@vue/test-utils';
|
||||||
import Hello from '../hello.vue';
|
import Hello from './hello.vue';
|
||||||
|
|
||||||
describe('Hello', () => {
|
describe('Hello', () => {
|
||||||
it('renders properly', () => {
|
it('renders properly', () => {
|
||||||
@ -43,16 +37,10 @@ defineProps<{}>();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Hello!</p>
|
||||||
<p>Welcome to Hello!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -60,7 +48,7 @@ exports[`component should generate files with vitest 2`] = `
|
|||||||
"import { describe, it, expect } from 'vitest';
|
"import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import Hello from '../hello.vue';
|
import Hello from './hello.vue';
|
||||||
|
|
||||||
describe('Hello', () => {
|
describe('Hello', () => {
|
||||||
it('renders properly', () => {
|
it('renders properly', () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { logger, readJson, Tree } from '@nx/devkit';
|
import { logger, Tree } from '@nx/devkit';
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import { componentGenerator } from './component';
|
import { componentGenerator } from './component';
|
||||||
import { createLib } from '../../utils/test-utils';
|
import { createLib } from '../../utils/test-utils';
|
||||||
@ -30,17 +30,14 @@ describe('component', () => {
|
|||||||
appTree.exists('my-lib/src/components/hello/hello.vue')
|
appTree.exists('my-lib/src/components/hello/hello.vue')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('my-lib/src/components/hello/__tests__/hello.spec.ts')
|
appTree.exists('my-lib/src/components/hello/hello.spec.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.read('my-lib/src/components/hello/hello.vue', 'utf-8')
|
appTree.read('my-lib/src/components/hello/hello.vue', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
appTree.read(
|
appTree.read('my-lib/src/components/hello/hello.spec.ts', 'utf-8')
|
||||||
'my-lib/src/components/hello/__tests__/hello.spec.ts',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,10 +52,7 @@ describe('component', () => {
|
|||||||
appTree.read('my-lib/src/components/hello/hello.vue', 'utf-8')
|
appTree.read('my-lib/src/components/hello/hello.vue', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
appTree.read(
|
appTree.read('my-lib/src/components/hello/hello.spec.ts', 'utf-8')
|
||||||
'my-lib/src/components/hello/__tests__/hello.spec.ts',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -114,7 +108,7 @@ describe('component', () => {
|
|||||||
appTree.exists('my-lib/src/components/hello/Hello.vue')
|
appTree.exists('my-lib/src/components/hello/Hello.vue')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('my-lib/src/components/hello/__tests__/Hello.spec.ts')
|
appTree.exists('my-lib/src/components/hello/Hello.spec.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -131,9 +125,7 @@ describe('component', () => {
|
|||||||
appTree.exists('my-lib/src/components/HelloWorld/HelloWorld.vue')
|
appTree.exists('my-lib/src/components/HelloWorld/HelloWorld.vue')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists('my-lib/src/components/HelloWorld/HelloWorld.spec.ts')
|
||||||
'my-lib/src/components/HelloWorld/__tests__/HelloWorld.spec.ts'
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { describe, it, expect } from 'vitest'
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import <%= className %> from '../<%= fileName %>.vue';
|
import <%= className %> from './<%= fileName %>.vue';
|
||||||
|
|
||||||
describe('<%= className %>', () => {
|
describe('<%= className %>', () => {
|
||||||
it('renders properly', () => {
|
it('renders properly', () => {
|
||||||
@ -3,13 +3,8 @@ defineProps<{}>()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to <%= className %>!</p>
|
||||||
<p>Welcome to <%= className %>!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
</style>
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -163,9 +163,7 @@ describe('lib', () => {
|
|||||||
expect(tree.exists('my-lib/package.json')).toBeFalsy();
|
expect(tree.exists('my-lib/package.json')).toBeFalsy();
|
||||||
expect(tree.exists('my-lib/src/index.ts')).toBeTruthy();
|
expect(tree.exists('my-lib/src/index.ts')).toBeTruthy();
|
||||||
expect(tree.exists('my-lib/src/components/my-lib.vue')).toBeTruthy();
|
expect(tree.exists('my-lib/src/components/my-lib.vue')).toBeTruthy();
|
||||||
expect(
|
expect(tree.exists('my-lib/src/components/my-lib.spec.ts')).toBeTruthy();
|
||||||
tree.exists('my-lib/src/components/__tests__/my-lib.spec.ts')
|
|
||||||
).toBeTruthy();
|
|
||||||
const eslintJson = readJson(tree, 'my-lib/.eslintrc.json');
|
const eslintJson = readJson(tree, 'my-lib/.eslintrc.json');
|
||||||
expect(eslintJson).toMatchSnapshot();
|
expect(eslintJson).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
@ -206,9 +204,7 @@ describe('lib', () => {
|
|||||||
tree.exists('my-dir/my-lib/src/components/my-dir-my-lib.vue')
|
tree.exists('my-dir/my-lib/src/components/my-dir-my-lib.vue')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
tree.exists(
|
tree.exists('my-dir/my-lib/src/components/my-dir-my-lib.spec.ts')
|
||||||
'my-dir/my-lib/src/components/__tests__/my-dir-my-lib.spec.ts'
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -44,15 +44,10 @@ describe('vue:component-story', () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Vlv!</p>
|
||||||
<p>Welcome to Vlv!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
@ -88,15 +83,10 @@ describe('vue:component-story', () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Vlv!</p>
|
||||||
<p>Welcome to Vlv!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,15 +13,10 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Vlv!</p>
|
||||||
<p>Welcome to Vlv!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -44,7 +39,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.read('test-ui-app/src/components/NxWelcome.stories.ts', 'utf-8')
|
appTree.read('test-ui-app/src/app/NxWelcome.stories.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
appTree.read(
|
appTree.read(
|
||||||
@ -61,7 +56,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.read('test-ui-app/src/components/NxWelcome.stories.ts', 'utf-8')
|
appTree.read('test-ui-app/src/app/NxWelcome.stories.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
appTree.read(
|
appTree.read(
|
||||||
@ -73,7 +68,7 @@ describe('vue:stories for applications', () => {
|
|||||||
|
|
||||||
it('should not update existing stories', async () => {
|
it('should not update existing stories', async () => {
|
||||||
appTree.write(
|
appTree.write(
|
||||||
'test-ui-app/src/components/NxWelcome.stories.ts',
|
'test-ui-app/src/app/NxWelcome.stories.ts',
|
||||||
`import { ComponentStory, ComponentMeta } from '@storybook/vue3'`
|
`import { ComponentStory, ComponentMeta } from '@storybook/vue3'`
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -82,7 +77,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.read('test-ui-app/src/components/NxWelcome.stories.ts', 'utf-8')
|
appTree.read('test-ui-app/src/app/NxWelcome.stories.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +99,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('test-ui-app/src/components/NxWelcome.stories.ts')
|
appTree.exists('test-ui-app/src/app/NxWelcome.stories.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists(
|
||||||
@ -135,7 +130,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('test-ui-app/src/components/NxWelcome.stories.ts')
|
appTree.exists('test-ui-app/src/app/NxWelcome.stories.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists(
|
||||||
@ -166,7 +161,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('test-ui-app/src/components/NxWelcome.stories.ts')
|
appTree.exists('test-ui-app/src/app/NxWelcome.stories.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists(
|
||||||
@ -194,7 +189,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('test-ui-app/src/components/NxWelcome.stories.ts')
|
appTree.exists('test-ui-app/src/app/NxWelcome.stories.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists(
|
||||||
@ -229,7 +224,7 @@ describe('vue:stories for applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
appTree.exists('test-ui-app/src/components/NxWelcome.stories.ts')
|
appTree.exists('test-ui-app/src/app/NxWelcome.stories.ts')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
appTree.exists(
|
appTree.exists(
|
||||||
|
|||||||
@ -13,15 +13,10 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Vlv!</p>
|
||||||
<p>Welcome to Vlv!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import {
|
|||||||
visitNotIgnoredFiles,
|
visitNotIgnoredFiles,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { basename, join } from 'path';
|
import { basename, join } from 'path';
|
||||||
import minimatch = require('minimatch');
|
|
||||||
import { nxVersion } from '../../utils/versions';
|
import { nxVersion } from '../../utils/versions';
|
||||||
import { createComponentStories } from './lib/component-story';
|
import { createComponentStories } from './lib/component-story';
|
||||||
|
import minimatch = require('minimatch');
|
||||||
|
|
||||||
export interface StorybookStoriesSchema {
|
export interface StorybookStoriesSchema {
|
||||||
project: string;
|
project: string;
|
||||||
@ -36,22 +36,28 @@ export async function createAllStories(
|
|||||||
) {
|
) {
|
||||||
const { sourceRoot, root } = projectConfiguration;
|
const { sourceRoot, root } = projectConfiguration;
|
||||||
let componentPaths: string[] = [];
|
let componentPaths: string[] = [];
|
||||||
const projectPath = joinPathFragments(sourceRoot, 'components');
|
const pathsToCheck = [
|
||||||
visitNotIgnoredFiles(tree, projectPath, (path) => {
|
joinPathFragments(sourceRoot, 'app'), // Default component folder for apps
|
||||||
// Ignore private files starting with "_".
|
joinPathFragments(sourceRoot, 'components'), // Additional component folder
|
||||||
if (basename(path).startsWith('_')) return;
|
];
|
||||||
if (ignorePaths?.some((pattern) => minimatch(path, pattern))) return;
|
|
||||||
if (path.endsWith('.vue')) {
|
|
||||||
// Let's see if the .stories.* file exists
|
|
||||||
const ext = path.slice(path.lastIndexOf('.'));
|
|
||||||
const storyPathJs = `${path.split(ext)[0]}.stories.js`;
|
|
||||||
const storyPathTs = `${path.split(ext)[0]}.stories.ts`;
|
|
||||||
|
|
||||||
if (!tree.exists(storyPathJs) && !tree.exists(storyPathTs)) {
|
for (const p of pathsToCheck) {
|
||||||
componentPaths.push(path);
|
visitNotIgnoredFiles(tree, p, (path) => {
|
||||||
|
// Ignore private files starting with "_".
|
||||||
|
if (basename(path).startsWith('_')) return;
|
||||||
|
if (ignorePaths?.some((pattern) => minimatch(path, pattern))) return;
|
||||||
|
if (path.endsWith('.vue')) {
|
||||||
|
// Let's see if the .stories.* file exists
|
||||||
|
const ext = path.slice(path.lastIndexOf('.'));
|
||||||
|
const storyPathJs = `${path.split(ext)[0]}.stories.js`;
|
||||||
|
const storyPathTs = `${path.split(ext)[0]}.stories.ts`;
|
||||||
|
|
||||||
|
if (!tree.exists(storyPathJs) && !tree.exists(storyPathTs)) {
|
||||||
|
componentPaths.push(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
componentPaths.map(async (componentPath) => {
|
componentPaths.map(async (componentPath) => {
|
||||||
|
|||||||
@ -22,15 +22,10 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<p>Welcome to Vlv!</p>
|
||||||
<p>Welcome to Vlv!</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
div {
|
|
||||||
color: pink;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user