pull/2368/head
Aniket-Engg 3 years ago committed by yann300
parent 0a51ed95fc
commit f6c447ff8d
  1. 2
      apps/remix-ide/webpack.config.js
  2. 11
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  3. 12
      libs/remix-ws-templates/.eslintrc
  4. 7
      libs/remix-ws-templates/README.md
  5. 8
      libs/remix-ws-templates/erc20/index.js
  6. 3
      libs/remix-ws-templates/index.js
  7. 20
      libs/remix-ws-templates/package.json
  8. 3
      libs/remix-ws-templates/src/index.ts
  9. 1
      libs/remix-ws-templates/src/templates/blank/index.ts
  10. 0
      libs/remix-ws-templates/src/templates/erc20/contracts/SampleERC20.sol
  11. 18
      libs/remix-ws-templates/src/templates/erc20/index.ts
  12. 2
      libs/remix-ws-templates/src/templates/erc20/scripts/deploy_with_ethers.ts
  13. 2
      libs/remix-ws-templates/src/templates/erc20/scripts/deploy_with_web3.ts
  14. 9
      libs/remix-ws-templates/src/templates/erc20/scripts/ethers.ts
  15. 6
      libs/remix-ws-templates/src/templates/erc20/scripts/web3.ts
  16. 4
      libs/remix-ws-templates/src/templates/erc20/tests/SampleERC20_test.sol
  17. 2
      libs/remix-ws-templates/src/templates/remixDefault/README.txt
  18. 0
      libs/remix-ws-templates/src/templates/remixDefault/contracts/1_Storage.sol
  19. 8
      libs/remix-ws-templates/src/templates/remixDefault/contracts/2_Owner.sol
  20. 4
      libs/remix-ws-templates/src/templates/remixDefault/contracts/3_Ballot.sol
  21. 0
      libs/remix-ws-templates/src/templates/remixDefault/index.ts
  22. 2
      libs/remix-ws-templates/src/templates/remixDefault/scripts/deploy_with_ethers.ts
  23. 2
      libs/remix-ws-templates/src/templates/remixDefault/scripts/deploy_with_web3.ts
  24. 4
      libs/remix-ws-templates/src/templates/remixDefault/scripts/ethers.ts
  25. 2
      libs/remix-ws-templates/src/templates/remixDefault/scripts/web3.ts
  26. 8
      libs/remix-ws-templates/src/templates/remixDefault/tests/Ballot_test.sol
  27. 0
      libs/remix-ws-templates/src/templates/remixDefault/tests/storage.test.js
  28. 4
      libs/remix-ws-templates/src/types/index.d.ts
  29. 7
      libs/remix-ws-templates/tsconfig.json
  30. 15
      libs/remix-ws-templates/tsconfig.lib.json
  31. 5
      nx.json
  32. 15
      tsconfig.base.json
  33. 57
      workspace.json

@ -18,7 +18,9 @@ module.exports = config => {
nxWebpackConfig.module.rules.push({ test: /\.txt$/, use: 'raw-loader' }) nxWebpackConfig.module.rules.push({ test: /\.txt$/, use: 'raw-loader' })
nxWebpackConfig.module.rules.push({ test: /\.sol$/, use: 'raw-loader' }) nxWebpackConfig.module.rules.push({ test: /\.sol$/, use: 'raw-loader' })
nxWebpackConfig.module.rules.push({ test: /\.test\.js$/, use: 'raw-loader' }) nxWebpackConfig.module.rules.push({ test: /\.test\.js$/, use: 'raw-loader' })
nxWebpackConfig.module.rules.push({ test: /\web3.ts$/, use: 'raw-loader' }) nxWebpackConfig.module.rules.push({ test: /\web3.ts$/, use: 'raw-loader' })
nxWebpackConfig.module.rules.push({ test: /\ethers.ts$/, use: 'raw-loader' }) nxWebpackConfig.module.rules.push({ test: /\ethers.ts$/, use: 'raw-loader' })

@ -6,6 +6,7 @@ import { checkSlash, checkSpecialChars } from '@remix-ui/helper'
import { JSONStandardInput, WorkspaceTemplate } from '../types' import { JSONStandardInput, WorkspaceTemplate } from '../types'
import { QueryParams } from '@remix-project/remix-lib' import { QueryParams } from '@remix-project/remix-lib'
import * as templateWithContent from '@remix-project/remix-ws-templates'
const LOCALHOST = ' - connect to localhost - ' const LOCALHOST = ' - connect to localhost - '
@ -151,13 +152,17 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
default: default:
try { try {
// const templateWithContent = await import('../templates') console.log('templateWithContent--->', templateWithContent)
const templateWithContent = await import('remix-ws-templates')
const templateList = Object.keys(templateWithContent) const templateList = Object.keys(templateWithContent)
if (!templateList.includes(template)) break if (!templateList.includes(template)) break
const files = templateWithContent[template] console.log('templateWithContent--->', templateWithContent[template])
// @ts-ignore
const files = await templateWithContent[template]()
console.log('files--->', files)
for (const file in files) { for (const file in files) {
try { try {
// const f2 = await import(files[file])
// console.log('files-with await f2-->', f2)
await workspaceProvider.set(file, files[file]) await workspaceProvider.set(file, files[file])
} catch (error) { } catch (error) {
console.error(error) console.error(error)

@ -0,0 +1,12 @@
{
"extends": "../../.eslintrc",
"rules": {
},
"env": {
"browser": true,
"amd": true,
"node": true,
"es6": true
},
"ignorePatterns": ["!**/*"]
}

@ -0,0 +1,7 @@
# remix-ws-templates
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test remix-ws-templates` to execute the unit tests via [Jest](https://jestjs.io).

@ -1,8 +0,0 @@
export default {
'contracts/SampleERC20.sol': require('./contracts/SampleERC20.sol').default,
'scripts/deploy_with_ethers.ts': require('./scripts/deploy_with_ethers.ts').default,
'scripts/deploy_with_web3.ts': require('./scripts/deploy_with_web3.ts').default,
'scripts/ethers.ts': require('./scripts/ethers.ts').default,
'scripts/web3.ts': require('./scripts/web3.ts').default,
'tests/SampleERC20_test.sol': require('./tests/SampleERC20_test.sol').default
}

@ -1,3 +0,0 @@
export { default as remixDefault } from './remixDefault'
export { default as erc20 } from './erc20'
// export { default as blank } from './blank'

@ -1,11 +1,11 @@
{ {
"name": "remix-ws-templates", "name": "@remix-project/remix-ws-templates",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "src/index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "Aniket-Engg", "author": "Aniket-Engg",
"license": "ISC" "license": "ISC"
} }

@ -0,0 +1,3 @@
export { default as remixDefault } from './templates/remixDefault'
export { default as erc20 } from './templates/erc20'
export { default as blank } from './templates/blank'

@ -0,0 +1,18 @@
export default async () => {
// @ts-ignore
console.log('------>', await import('raw-loader!./contracts/SampleERC20.sol'))
return {
// @ts-ignore
'contracts/SampleERC20.sol': (await import('raw-loader!./contracts/SampleERC20.sol')).default,
// @ts-ignore
'scripts/deploy_with_ethers.ts': (await import('raw-loader!./scripts/deploy_with_ethers.ts')).default,
// @ts-ignore
'scripts/deploy_with_web3.ts': (await import('raw-loader!./scripts/deploy_with_web3.ts')).default,
// @ts-ignore
'scripts/ethers.ts': (await import('raw-loader!./scripts/ethers.ts')).default,
// @ts-ignore
'scripts/web3.ts': (await import('raw-loader!./scripts/web3.ts')).default,
// @ts-ignore
'tests/SampleERC20_test.sol': (await import('raw-loader!./tests/SampleERC20_test.sol')).default
}
}

@ -1,4 +1,4 @@
import { deploy } from './ethers.ts' import { deploy } from './ethers'
(async () => { (async () => {
try { try {

@ -1,4 +1,4 @@
import { deploy } from './web3.ts' import { deploy } from './web3'
(async () => { (async () => {
try { try {

@ -1,5 +1,8 @@
export const deploy = async (contractName: string, args: Array<any>, from?: string) => {
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -21,4 +24,4 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed() await contract.deployed()
return contract return contract
}: Promise<any> }

@ -1,5 +1,5 @@
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number) => { export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -21,4 +21,4 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
gas: gas || 1500000 gas: gas || 1500000
}) })
return newContractInstance.options return newContractInstance.options
}: Promise<any> }

@ -5,12 +5,12 @@ import "remix_tests.sol";
import "../contracts/SampleERC20.sol"; import "../contracts/SampleERC20.sol";
contract SampleERC20Test { contract SampleERC20Test {
SampleERC20 s; SampleERC20 s;
function beforeAll () public { function beforeAll () public {
s = new SampleERC20("TestToken", "TST"); s = new SampleERC20("TestToken", "TST");
} }
function testTokenNameAndSymbol () public { function testTokenNameAndSymbol () public {
Assert.equal(s.name(), "TestToken", "token name did not match"); Assert.equal(s.name(), "TestToken", "token name did not match");
Assert.equal(s.symbol(), "TST", "token symbol did not match"); Assert.equal(s.symbol(), "TST", "token symbol did not match");

@ -23,4 +23,4 @@ Output from script will appear in remix terminal.
Please note, 'require' statement is supported in a limited manner for Remix supported modules. Please note, 'require' statement is supported in a limited manner for Remix supported modules.
For now, modules supported by Remix are ethers, web3, swarmgw, chai, remix and hardhat only for hardhat.ethers object/plugin. For now, modules supported by Remix are ethers, web3, swarmgw, chai, remix and hardhat only for hardhat.ethers object/plugin.
For unsupported modules, an error like this will be thrown: '<module_name> module require is not supported by Remix IDE will be shown.' For unsupported modules, an error like this will be thrown: '<module_name> module require is not supported by Remix IDE will be shown.'

@ -11,10 +11,10 @@ import "hardhat/console.sol";
contract Owner { contract Owner {
address private owner; address private owner;
// event for EVM logging // event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner); event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner // modifier to check if caller is owner
modifier isOwner() { modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all // If the first argument of 'require' evaluates to 'false', execution terminates and all
@ -25,7 +25,7 @@ contract Owner {
require(msg.sender == owner, "Caller is not owner"); require(msg.sender == owner, "Caller is not owner");
_; _;
} }
/** /**
* @dev Set contract deployer as owner * @dev Set contract deployer as owner
*/ */
@ -51,4 +51,4 @@ contract Owner {
function getOwner() external view returns (address) { function getOwner() external view returns (address) {
return owner; return owner;
} }
} }

@ -7,7 +7,7 @@ pragma solidity >=0.7.0 <0.9.0;
* @dev Implements voting process along with vote delegation * @dev Implements voting process along with vote delegation
*/ */
contract Ballot { contract Ballot {
struct Voter { struct Voter {
uint weight; // weight is accumulated by delegation uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted bool voted; // if true, that person already voted
@ -46,7 +46,7 @@ contract Ballot {
})); }));
} }
} }
/** /**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. * @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter * @param voter address of voter

@ -2,7 +2,7 @@
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script. // Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S // And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './ethers.ts' import { deploy } from './ethers'
(async () => { (async () => {
try { try {

@ -2,7 +2,7 @@
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script. // Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S // And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './web3.ts' import { deploy } from './web3'
(async () => { (async () => {
try { try {

@ -1,5 +1,5 @@
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -15,7 +15,7 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
if (from) { if (from) {
contract = await factory.connect(from).deploy(...args); contract = await factory.connect(from).deploy(...args);
} else { } else {
contract = await factory.deploy(...arguments); contract = await factory.deploy(...args);
} }
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined

@ -1,5 +1,5 @@
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => { export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated

@ -6,22 +6,22 @@ import "hardhat/console.sol";
import "../contracts/3_Ballot.sol"; import "../contracts/3_Ballot.sol";
contract BallotTest { contract BallotTest {
bytes32[] proposalNames; bytes32[] proposalNames;
Ballot ballotToTest; Ballot ballotToTest;
function beforeAll () public { function beforeAll () public {
proposalNames.push(bytes32("candidate1")); proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames); ballotToTest = new Ballot(proposalNames);
} }
function checkWinningProposal () public { function checkWinningProposal () public {
console.log("Running checkWinningProposal"); console.log("Running checkWinningProposal");
ballotToTest.vote(0); ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal"); Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name"); Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
} }
function checkWinninProposalWithReturnValue () public view returns (bool) { function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0; return ballotToTest.winningProposal() == 0;
} }

@ -0,0 +1,4 @@
declare var remix:any
declare var ethers:any
declare var web3:any
declare var web3Provider:any

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node"],
},
"include": ["**/*.ts"]
}

@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"rootDir": "./src",
"types": ["node"]
},
"exclude": [
"**/*.spec.ts",
"tests/"
],
"include": ["**/*.ts", "**/*.sol"]
}

@ -133,7 +133,7 @@
"remix-ui-app": { "remix-ui-app": {
"tags": [] "tags": []
}, },
"remix-ui-helper": { "remix-ui-helper": {
"tags": [] "tags": []
}, },
"remix-ui-vertical-icons-panel": { "remix-ui-vertical-icons-panel": {
@ -165,6 +165,9 @@
}, },
"remix-ui-permission-handler": { "remix-ui-permission-handler": {
"tags": [] "tags": []
},
"remix-ws-templates": {
"tags": []
} }
}, },
"targetDependencies": { "targetDependencies": {

@ -75,10 +75,19 @@
], ],
"@remix-ui/theme-module": ["libs/remix-ui/theme-module/src/index.ts"], "@remix-ui/theme-module": ["libs/remix-ui/theme-module/src/index.ts"],
"@remix-ui/panel": ["libs/remix-ui/panel/src/index.ts"], "@remix-ui/panel": ["libs/remix-ui/panel/src/index.ts"],
"@remix-ui/editor-context-view": ["libs/remix-ui/editor-context-view/src/index.ts"], "@remix-ui/editor-context-view": [
"@remix-ui/solidity-unit-testing": ["libs/remix-ui/solidity-unit-testing/src/index.ts"], "libs/remix-ui/editor-context-view/src/index.ts"
],
"@remix-ui/solidity-unit-testing": [
"libs/remix-ui/solidity-unit-testing/src/index.ts"
],
"@remix-ui/run-tab": ["libs/remix-ui/run-tab/src/index.ts"], "@remix-ui/run-tab": ["libs/remix-ui/run-tab/src/index.ts"],
"@remix-ui/permission-handler": ["libs/remix-ui/permission-handler/src/index.ts"] "@remix-ui/permission-handler": [
"libs/remix-ui/permission-handler/src/index.ts"
],
"@remix-project/remix-ws-templates": [
"libs/remix-ws-templates/src/index.ts"
]
} }
}, },
"exclude": ["node_modules", "tmp"] "exclude": ["node_modules", "tmp"]

@ -1,4 +1,3 @@
{ {
"version": 1, "version": 1,
"projects": { "projects": {
@ -756,8 +755,8 @@
} }
}, },
"remix-ui-plugin-manager": { "remix-ui-plugin-manager": {
"root": "libs/remix-ui/plugin-manager", "root": "libs/remix-ui/plugin-manager",
"sourceRoot": "libs/remix-ui/plugin-manager/src", "sourceRoot": "libs/remix-ui/plugin-manager/src",
"projectType": "library", "projectType": "library",
"schematics": {}, "schematics": {},
"architect": { "architect": {
@ -1010,7 +1009,6 @@
"tsConfig": ["libs/remix-ui/home-tab/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/home-tab/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/home-tab/**/*"] "exclude": ["**/node_modules/**", "!libs/remix-ui/home-tab/**/*"]
} }
} }
} }
}, },
@ -1039,8 +1037,8 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"] "exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"]
} }
} }
} }
@ -1101,8 +1099,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/vertical-icons-panel/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/vertical-icons-panel/**/*"] "libs/remix-ui/vertical-icons-panel/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/vertical-icons-panel/**/*"
]
} }
} }
} }
@ -1136,17 +1139,19 @@
} }
} }
} }
}, },
"solidity-unit-testing": { "solidity-unit-testing": {
"root": "libs/remix-ui/solidity-unit-testing", "root": "libs/remix-ui/solidity-unit-testing",
"sourceRoot": "libs/remix-ui/solidity-unit-testing/src", "sourceRoot": "libs/remix-ui/solidity-unit-testing/src",
"projectType": "library", "projectType": "library",
"architect": { "architect": {
"lint": { "lint": {
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/solidity-unit-testing/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/solidity-unit-testing/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/solidity-unit-testing/**/*" "!libs/remix-ui/solidity-unit-testing/**/*"
@ -1165,7 +1170,10 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/editor-context-view/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/editor-context-view/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor-context-view/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor-context-view/**/*"
]
} }
} }
} }
@ -1180,7 +1188,11 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/run-tab/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/run-tab/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-ui/run-tab/**/*.d.ts", "!libs/remix-ui/run-tab/**/*"] "exclude": [
"**/node_modules/**",
"libs/remix-ui/run-tab/**/*.d.ts",
"!libs/remix-ui/run-tab/**/*"
]
} }
} }
} }
@ -1195,7 +1207,24 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/permission-handler/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/permission-handler/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-ui/permission-handler/**/*.d.ts", "!libs/remix-ui/permission-handler/**/*"] "exclude": [
"**/node_modules/**",
"libs/remix-ui/permission-handler/**/*.d.ts",
"!libs/remix-ui/permission-handler/**/*"
]
}
}
}
},
"remix-ws-templates": {
"root": "libs/remix-ws-templates",
"sourceRoot": "libs/remix-ws-templates/src",
"projectType": "library",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/remix-ws-templates/**/*.ts"]
} }
} }
} }

Loading…
Cancel
Save