зеркало из https://github.com/microsoft/lage.git
Use @swc/jest and fix debugging tests (#738)
This commit is contained in:
Родитель
4a4113efec
Коммит
7453924a5a
|
@ -26,14 +26,15 @@
|
|||
"protocol": "inspector"
|
||||
},
|
||||
{
|
||||
"name": "Debug current open test",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Jest",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "node_modules/jest/bin/jest.js",
|
||||
"outFiles": ["${workspaceFolder}/**/*.js"],
|
||||
"args": ["--runInBand", "${fileBasenameNoExtension}"],
|
||||
"sourceMaps": true
|
||||
"program": "${workspaceRoot}/scripts/debugTests.js",
|
||||
"cwd": "${fileDirname}",
|
||||
"args": ["--runInBand", "--watch", "${fileBasename}"],
|
||||
"sourceMaps": true,
|
||||
"outputCapture": "std",
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// @ts-check
|
||||
const { getWorkspaceRoot, getPackageInfos } = require("workspace-tools");
|
||||
const { findProjectRoot, getPackageInfos } = require("workspace-tools");
|
||||
const path = require("path");
|
||||
const swcOptions = require("./swc");
|
||||
|
||||
const root = getWorkspaceRoot(process.cwd()) ?? process.cwd();
|
||||
const root = findProjectRoot(process.cwd()) ?? process.cwd();
|
||||
const packages = getPackageInfos(root);
|
||||
const moduleNameMapper = Object.values(packages).reduce((acc, { packageJsonPath, name }) => {
|
||||
const packagePath = path.dirname(packageJsonPath);
|
||||
|
@ -13,26 +14,14 @@ const moduleNameMapper = Object.values(packages).reduce((acc, { packageJsonPath,
|
|||
|
||||
moduleNameMapper["^(\\.{1,2}/.*)\\.js$"] = "$1";
|
||||
|
||||
/** @type {import("ts-jest").JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
/** @type {import("jest").Config} */
|
||||
const config = {
|
||||
clearMocks: true,
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: ["src/**/*.ts", "!src/types/*.ts", "!**/node_modules/**"],
|
||||
coverageDirectory: "coverage",
|
||||
coverageProvider: "v8",
|
||||
extensionsToTreatAsEsm: [".ts"],
|
||||
testMatch: ["**/?(*.)+(spec|test).ts?(x)"],
|
||||
testPathIgnorePatterns: ["/node_modules/"],
|
||||
transform: {
|
||||
"^.+\\.tsx?$": [
|
||||
"ts-jest",
|
||||
{
|
||||
isolatedModules: true,
|
||||
tsconfig: {
|
||||
jsx: "react",
|
||||
},
|
||||
},
|
||||
],
|
||||
"^.+\\.tsx?$": ["@swc/jest", /** @type {*} */ (swcOptions)],
|
||||
},
|
||||
transformIgnorePatterns: ["/node_modules/", "\\.pnp\\.[^\\/]+$"],
|
||||
watchPathIgnorePatterns: ["/node_modules/"],
|
||||
|
@ -40,3 +29,4 @@ module.exports = {
|
|||
...(process.env.LAGE_PACKAGE_NAME && { maxWorkers: 1 }),
|
||||
setupFilesAfterEnv: [path.join(__dirname, "jest-setup-after-env.js")],
|
||||
};
|
||||
module.exports = config;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// @ts-check
|
||||
/**
|
||||
* Options passed to swc.transformFile
|
||||
* @type {import("@swc/core").Options}
|
||||
*/
|
||||
const options = {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
tsx: false,
|
||||
dynamicImport: true,
|
||||
},
|
||||
target: "es2020",
|
||||
},
|
||||
module: {
|
||||
type: "commonjs",
|
||||
ignoreDynamic: true,
|
||||
},
|
||||
};
|
||||
module.exports = options;
|
|
@ -0,0 +1,30 @@
|
|||
// @ts-check
|
||||
const { run: runJest } = require("jest");
|
||||
const path = require("path");
|
||||
const { findPackageRoot } = require("workspace-tools");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function start() {
|
||||
const packagePath = findPackageRoot(process.cwd());
|
||||
if (!packagePath) {
|
||||
throw new Error("Could not find package.json relative to " + process.cwd());
|
||||
}
|
||||
|
||||
console.log(`Starting Jest debugging at: ${packagePath}`);
|
||||
|
||||
return runJest([
|
||||
"--config",
|
||||
path.join(packagePath, "jest.config.js"),
|
||||
"--rootDir",
|
||||
packagePath,
|
||||
"--runInBand",
|
||||
"--testTimeout=999999999",
|
||||
...args,
|
||||
]);
|
||||
}
|
||||
|
||||
start().catch((err) => {
|
||||
console.error(err?.stack || err);
|
||||
process.exit(1);
|
||||
});
|
|
@ -7,9 +7,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@swc/core": "^1.3.14",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"depcheck": "^1.4.3",
|
||||
"jest": "^29.0.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint-plugin-file-extension-in-import-ts": "^1.0.1",
|
||||
"workspace-tools": "0.36.4",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// @ts-check
|
||||
const path = require("path");
|
||||
const fs = require("fs/promises");
|
||||
const swc = require("@swc/core");
|
||||
const swcOptions = require("../config/swc");
|
||||
|
||||
module.exports = async function transpile(data) {
|
||||
const { target } = data;
|
||||
|
@ -22,20 +24,7 @@ module.exports = async function transpile(data) {
|
|||
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "lib" && entry.name !== "tests" && entry.name !== "dist") {
|
||||
queue.push(fullPath);
|
||||
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"))) {
|
||||
const swcOutput = await swc.transformFile(fullPath, {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
tsx: false,
|
||||
dynamicImport: true,
|
||||
},
|
||||
target: "es2020",
|
||||
},
|
||||
module: {
|
||||
type: "commonjs",
|
||||
ignoreDynamic: true,
|
||||
},
|
||||
});
|
||||
const swcOutput = await swc.transformFile(fullPath, swcOptions);
|
||||
const dest = fullPath
|
||||
.replace(/([/\\])src/, "$1lib")
|
||||
.replace(".tsx", ".js")
|
||||
|
|
92
yarn.lock
92
yarn.lock
|
@ -683,6 +683,13 @@
|
|||
slash "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
"@jest/create-cache-key-function@^29.7.0":
|
||||
version "29.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0"
|
||||
integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==
|
||||
dependencies:
|
||||
"@jest/types" "^29.6.3"
|
||||
|
||||
"@jest/environment@^29.5.0":
|
||||
version "29.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65"
|
||||
|
@ -760,12 +767,12 @@
|
|||
strip-ansi "^6.0.0"
|
||||
v8-to-istanbul "^9.0.1"
|
||||
|
||||
"@jest/schemas@^29.4.3":
|
||||
version "29.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788"
|
||||
integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==
|
||||
"@jest/schemas@^29.4.3", "@jest/schemas@^29.6.3":
|
||||
version "29.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
|
||||
integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
|
||||
dependencies:
|
||||
"@sinclair/typebox" "^0.25.16"
|
||||
"@sinclair/typebox" "^0.27.8"
|
||||
|
||||
"@jest/source-map@^29.4.3":
|
||||
version "29.4.3"
|
||||
|
@ -817,12 +824,12 @@
|
|||
slash "^3.0.0"
|
||||
write-file-atomic "^4.0.2"
|
||||
|
||||
"@jest/types@^29.5.0":
|
||||
version "29.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593"
|
||||
integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==
|
||||
"@jest/types@^29.5.0", "@jest/types@^29.6.3":
|
||||
version "29.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
|
||||
integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
|
||||
dependencies:
|
||||
"@jest/schemas" "^29.4.3"
|
||||
"@jest/schemas" "^29.6.3"
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^3.0.0"
|
||||
"@types/node" "*"
|
||||
|
@ -908,10 +915,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.0.tgz#2c91791a9ba6ca0a0f4aaac5e45d58df13639ac8"
|
||||
integrity sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g==
|
||||
|
||||
"@sinclair/typebox@^0.25.16":
|
||||
version "0.25.24"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718"
|
||||
integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@sinonjs/commons@^2.0.0":
|
||||
version "2.0.0"
|
||||
|
@ -993,6 +1000,20 @@
|
|||
"@swc/core-win32-ia32-msvc" "1.3.56"
|
||||
"@swc/core-win32-x64-msvc" "1.3.56"
|
||||
|
||||
"@swc/counter@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
|
||||
integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
|
||||
|
||||
"@swc/jest@^0.2.36":
|
||||
version "0.2.36"
|
||||
resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.36.tgz#2797450a30d28b471997a17e901ccad946fe693e"
|
||||
integrity sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==
|
||||
dependencies:
|
||||
"@jest/create-cache-key-function" "^29.7.0"
|
||||
"@swc/counter" "^0.1.3"
|
||||
jsonc-parser "^3.2.0"
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
|
||||
|
@ -1613,13 +1634,6 @@ browserslist@^4.21.3:
|
|||
node-releases "^2.0.8"
|
||||
update-browserslist-db "^1.0.10"
|
||||
|
||||
bs-logger@0.x:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
|
||||
integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
|
||||
dependencies:
|
||||
fast-json-stable-stringify "2.x"
|
||||
|
||||
bser@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
|
||||
|
@ -2297,7 +2311,7 @@ fast-glob@3.3.1, fast-glob@^3.2.9, fast-glob@^3.3.1:
|
|||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
|
||||
fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
@ -3202,7 +3216,7 @@ jest-snapshot@^29.5.0:
|
|||
pretty-format "^29.5.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
jest-util@^29.0.0, jest-util@^29.5.0:
|
||||
jest-util@^29.5.0:
|
||||
version "29.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f"
|
||||
integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==
|
||||
|
@ -3310,11 +3324,16 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||
|
||||
json5@^2.1.3, json5@^2.2.2, json5@^2.2.3:
|
||||
json5@^2.1.3, json5@^2.2.2:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
jsonc-parser@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
|
||||
integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
|
@ -3422,11 +3441,6 @@ locate-path@^6.0.0:
|
|||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
lodash.memoize@4.x:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
|
||||
|
||||
lodash.merge@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
|
@ -3482,7 +3496,7 @@ make-dir@^3.0.0, make-dir@^3.0.2:
|
|||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
make-error@1.x, make-error@^1.1.1:
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
@ -4122,7 +4136,7 @@ semver-compare@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
||||
|
||||
semver@7.5.0, semver@7.x, semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
|
||||
semver@7.5.0, semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.0.tgz#ed8c5dc8efb6c629c88b23d41dc9bf40c1d96cd0"
|
||||
integrity sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
|
||||
|
@ -4478,20 +4492,6 @@ trim-repeated@^1.0.0:
|
|||
dependencies:
|
||||
escape-string-regexp "^1.0.2"
|
||||
|
||||
ts-jest@^29.0.0:
|
||||
version "29.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891"
|
||||
integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==
|
||||
dependencies:
|
||||
bs-logger "0.x"
|
||||
fast-json-stable-stringify "2.x"
|
||||
jest-util "^29.0.0"
|
||||
json5 "^2.2.3"
|
||||
lodash.memoize "4.x"
|
||||
make-error "1.x"
|
||||
semver "7.x"
|
||||
yargs-parser "^21.0.1"
|
||||
|
||||
ts-node@^10.9.1:
|
||||
version "10.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
|
||||
|
@ -4765,7 +4765,7 @@ yargs-parser@^20.2.2:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^21.0.0, yargs-parser@^21.0.1, yargs-parser@^21.1.1:
|
||||
yargs-parser@^21.0.0, yargs-parser@^21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
|
Загрузка…
Ссылка в новой задаче