Add source
This commit is contained in:
parent
ff1b6186cb
commit
5767e222cb
22 changed files with 425 additions and 1 deletions
7
.editorconfig
Normal file
7
.editorconfig
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{*.js,*.ts,*.tsx,*.json,*.css}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
28
.eslintrc.js
Normal file
28
.eslintrc.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: ["standard-with-typescript", "standard-jsx", "prettier"],
|
||||||
|
parserOptions: {
|
||||||
|
project: "./tsconfig.json",
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/member-delimiter-style": 2,
|
||||||
|
"@typescript-eslint/semi": ["error", "always"],
|
||||||
|
"@typescript-eslint/no-extra-semi": "error",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": 0,
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": 0,
|
||||||
|
"@typescript-eslint/no-floating-promises": 0,
|
||||||
|
"@typescript-eslint/prefer-nullish-coalescing": 0,
|
||||||
|
"@typescript-eslint/restrict-template-expressions": 0,
|
||||||
|
"@typescript-eslint/promise-function-async": 0,
|
||||||
|
"@typescript-eslint/consistent-type-definitions": 0,
|
||||||
|
"@typescript-eslint/no-misused-promises": ["error", {"checksConditionals": false}],
|
||||||
|
"@typescript-eslint/no-redeclare": 0,
|
||||||
|
"@typescript-eslint/no-non-null-assertion": 0,
|
||||||
|
"@typescript-eslint/member-delimiter-style": 0,
|
||||||
|
"import/no-absolute-path": 0,
|
||||||
|
"no-return-assign": 0,
|
||||||
|
"no-useless-return": 0,
|
||||||
|
"quotes": 0,
|
||||||
|
"object-curly-spacing": 2,
|
||||||
|
"n/no-callback-literal": 0,
|
||||||
|
},
|
||||||
|
};
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
node_modules/
|
2
.projectile
Normal file
2
.projectile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-node_modules/
|
||||||
|
-build/
|
46
README.md
Normal file
46
README.md
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
## About
|
||||||
|
|
||||||
|
Photon is a Google Chrome extension that provides information about
|
||||||
|
the public IP address you are using to connect to the internet. When
|
||||||
|
the extension's icon is clicked, the extension makes a request to the
|
||||||
|
[clean.wtfismyip.com](https://clean.wtfismyip.com)
|
||||||
|
web service to discover information about your public IP address, and
|
||||||
|
then displays that information in a simple HTML view.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Displays a public IP address.
|
||||||
|
* Displays the Internet Service Provider (ISP) associated with a public IP address.
|
||||||
|
* Displays the city associated with a public IP address.
|
||||||
|
* Displays the country associated with a public IP address.
|
||||||
|
* Displays whether or not a public IP address is a Tor exit node.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Produce the "build" directory:
|
||||||
|
|
||||||
|
git clone https://github.com/0x1eef/photon.git
|
||||||
|
cd photon
|
||||||
|
npm i
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
Add the extension to Chrome:
|
||||||
|
|
||||||
|
* Visit `chrome://extensions`.
|
||||||
|
* Check "Developer mode" (top right hand corner).
|
||||||
|
* Click "Load unpacked extension".
|
||||||
|
* Choose the "build/" directory from the file dialog.
|
||||||
|
* Done.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
* [Source code (GitHub)](https://github.com/0x1eef/photon#readme)
|
||||||
|
* [Source code (GitLab)](https://gitlab.com/0x1eef/photon#about)
|
||||||
|
|
||||||
|
## <a id="license"> License </a>
|
||||||
|
|
||||||
|
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
|
||||||
|
<br>
|
||||||
|
See [LICENSE](./LICENSE)
|
||||||
|
|
||||||
|
|
32
package-lock.json
generated
32
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chrome": "^0.0.246",
|
||||||
"@types/react": "^18.0.18",
|
"@types/react": "^18.0.18",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
@ -224,6 +225,16 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chrome": {
|
||||||
|
"version": "0.0.246",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.246.tgz",
|
||||||
|
"integrity": "sha512-MxGxEomGxsJiL9xe/7ZwVgwdn8XVKWbPvxpVQl3nWOjrS0Ce63JsfzxUc4aU3GvRcUPYsfufHmJ17BFyKxeA4g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/filesystem": "*",
|
||||||
|
"@types/har-format": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.44.3",
|
"version": "8.44.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz",
|
||||||
|
@ -250,6 +261,21 @@
|
||||||
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
|
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/filesystem": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.33.tgz",
|
||||||
|
"integrity": "sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/filewriter": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/filewriter": {
|
||||||
|
"version": "0.0.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.30.tgz",
|
||||||
|
"integrity": "sha512-lB98tui0uxc7erbj0serZfJlHKLNJHwBltPnbmO1WRpL5T325GOHRiQfr2E29V2q+S1brDO63Fpdt6vb3bES9Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/glob": {
|
"node_modules/@types/glob": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
|
||||||
|
@ -260,6 +286,12 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/har-format": {
|
||||||
|
"version": "1.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.13.tgz",
|
||||||
|
"integrity": "sha512-PwBsCBD3lDODn4xpje3Y1di0aDJp4Ww7aSfMRVw6ysnxD4I7Wmq2mBkSKaDtN403hqH5sp6c9xQUvFYY3+lkBg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.13",
|
"version": "7.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "whatismyip",
|
"name": "photon",
|
||||||
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/chrome": "^0.0.246",
|
||||||
"@types/react": "^18.0.18",
|
"@types/react": "^18.0.18",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
|
26
src/_locales/en/messages.json
Normal file
26
src/_locales/en/messages.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"ip_address": {
|
||||||
|
"message": "IP Address"
|
||||||
|
},
|
||||||
|
"isp": {
|
||||||
|
"message": "ISP"
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"message": "City"
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"message": "Country"
|
||||||
|
},
|
||||||
|
"tor_exit_node": {
|
||||||
|
"message": "Tor exit node"
|
||||||
|
},
|
||||||
|
"loading": {
|
||||||
|
"message": "Loading..."
|
||||||
|
},
|
||||||
|
"yes": {
|
||||||
|
"message": "Yes"
|
||||||
|
},
|
||||||
|
"no": {
|
||||||
|
"message": "No"
|
||||||
|
}
|
||||||
|
}
|
10
src/css/components/Response.css
Normal file
10
src/css/components/Response.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
body .response {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .response .row {
|
||||||
|
display: flex;
|
||||||
|
place-content: space-between;
|
||||||
|
font-size: var(--default-font-size);
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
52
src/css/index.css
Normal file
52
src/css/index.css
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
:root {
|
||||||
|
/* Defaults */
|
||||||
|
--default-font-size: 1.15em;
|
||||||
|
--default-font-family: "SF Mono","Segoe UI Mono","Roboto Mono",Menlo,Courier,monospace;
|
||||||
|
|
||||||
|
/* Colors */
|
||||||
|
--primary-red: #d73e48;
|
||||||
|
--primary-green: #32b643;
|
||||||
|
--primary-blue: #302ecd;
|
||||||
|
--secondary-blue: #807fe2;
|
||||||
|
--primary-white: #FFF;
|
||||||
|
--secondary-white: #f1f1fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--default-font-family);
|
||||||
|
width: 368px;
|
||||||
|
height: 212px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root, .loader {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-blue) !important;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
color: var(--primary-blue) !important;
|
||||||
|
}
|
||||||
|
a:active, a:focus, a:hover {
|
||||||
|
color: var(--primary-blue) !important;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background: var(--secondary-white);
|
||||||
|
color: var(--primary-red);
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
place-content: center;
|
||||||
|
}
|
17
src/css/label.css
Normal file
17
src/css/label.css
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.label {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-rounded {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-error {
|
||||||
|
background: var(--primary-red);
|
||||||
|
color: var(--primary-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-success {
|
||||||
|
background: var(--primary-green);
|
||||||
|
color: var(--primary-white);
|
||||||
|
}
|
15
src/html/browseraction.html
Normal file
15
src/html/browseraction.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<link rel="stylesheet" href="/css/index.css"/>
|
||||||
|
<link rel="stylesheet" href="/css/label.css"/>
|
||||||
|
<link rel="stylesheet" href="/css/components/Response.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<footer>
|
||||||
|
Powered by <a href="https://clean.wtfismyip.com">clean.wtfismyip.com</a>
|
||||||
|
</footer>
|
||||||
|
<script src="/js/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
src/images/icons/icon128.png
Normal file
BIN
src/images/icons/icon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
src/images/icons/icon16.png
Normal file
BIN
src/images/icons/icon16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 321 B |
15
src/js/components/App.tsx
Normal file
15
src/js/components/App.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ResponseRenderer } from "/components/ResponseRenderer";
|
||||||
|
import { useWebService } from "/hooks/useWebService";
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
const [response, error] = useWebService();
|
||||||
|
const t = chrome.i18n.getMessage;
|
||||||
|
if (response) {
|
||||||
|
return (<ResponseRenderer response={response}/>);
|
||||||
|
} else if (error) {
|
||||||
|
/* FIXME: Add ErrorRenderer */
|
||||||
|
} else {
|
||||||
|
return (<div className="loader">{t("loading")}</div>);
|
||||||
|
}
|
||||||
|
}
|
39
src/js/components/ResponseRenderer.tsx
Normal file
39
src/js/components/ResponseRenderer.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
import { TResponse } from "/lib/response";
|
||||||
|
|
||||||
|
export function ResponseRenderer({ response }: {response: TResponse}) {
|
||||||
|
const t = chrome.i18n.getMessage;
|
||||||
|
|
||||||
|
function YesOrNoLabel({yes}: {yes: boolean}) {
|
||||||
|
if (yes) {
|
||||||
|
return (<label className="label label-rounded label-success">{t("yes")}</label>);
|
||||||
|
} else {
|
||||||
|
return (<label className="label label-rounded label-error">{t("no")}</label>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="response">
|
||||||
|
<div className="row">
|
||||||
|
<div>{t("ip_address")}</div>
|
||||||
|
<div>{response.IPAddress}</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div>{t("isp")}</div>
|
||||||
|
<div>{response.ISP}</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div>{t("city")}</div>
|
||||||
|
<div>{response.City}</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div>{t("country")}</div>
|
||||||
|
<div>{response.Country}</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div>{t("tor_exit_node")}</div>
|
||||||
|
<div>{<YesOrNoLabel yes={response.isTorExitNode}/>}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
19
src/js/hooks/useWebService.ts
Normal file
19
src/js/hooks/useWebService.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { TResponse, Response } from "/lib/response";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
type Maybe<T> = T | null;
|
||||||
|
|
||||||
|
export function useWebService(): [Maybe<TResponse>, Maybe<Error>] {
|
||||||
|
const endpoint = "https://wtfismyip.com/json";
|
||||||
|
const [response, setResponse] = useState<Maybe<TResponse>>(null);
|
||||||
|
const [error, setError] = useState<Maybe<Error>>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(endpoint)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) => setResponse(Response(json)))
|
||||||
|
.catch((err) => setError(err));
|
||||||
|
});
|
||||||
|
|
||||||
|
return [response, error];
|
||||||
|
}
|
8
src/js/index.tsx
Normal file
8
src/js/index.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { App } from "/components/App";
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const el: HTMLElement = document.getElementById("root")!;
|
||||||
|
ReactDOM.createRoot(el).render(<App/>);
|
||||||
|
});
|
30
src/js/lib/response.ts
Normal file
30
src/js/lib/response.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
export type TResponse = {
|
||||||
|
IPAddress: string;
|
||||||
|
Location: string;
|
||||||
|
ISP: string;
|
||||||
|
City: string;
|
||||||
|
Country: string;
|
||||||
|
isTorExitNode: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TServerResponse = {
|
||||||
|
YourFuckingIPAddress: string;
|
||||||
|
YourFuckingLocation: string;
|
||||||
|
YourFuckingISP: string;
|
||||||
|
YourFuckingCity: string;
|
||||||
|
YourFuckingCountry: string;
|
||||||
|
YourFuckingTorExit: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Response(res: TServerResponse): TResponse {
|
||||||
|
const self = Object.create(null);
|
||||||
|
|
||||||
|
self.IPAddress = res.YourFuckingIPAddress;
|
||||||
|
self.Location = res.YourFuckingLocation;
|
||||||
|
self.ISP = res.YourFuckingISP;
|
||||||
|
self.City = res.YourFuckingCity;
|
||||||
|
self.Country = res.YourFuckingCountry;
|
||||||
|
self.isTorExitNode = res.YourFuckingTorExit;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
15
src/manifest.json
Normal file
15
src/manifest.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Photon",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Provides various information about your public IP address.",
|
||||||
|
"action": {
|
||||||
|
"default_popup": "/html/browseraction.html"
|
||||||
|
},
|
||||||
|
"permissions": [],
|
||||||
|
"icons": {
|
||||||
|
"16": "images/icons/icon16.png",
|
||||||
|
"128": "images/icons/icon128.png"
|
||||||
|
},
|
||||||
|
"default_locale": "en"
|
||||||
|
}
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "ES2020",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"allowJs": true,
|
||||||
|
"lib": [ "ES2020", "DOM" ],
|
||||||
|
|
||||||
|
"baseUrl": "src/",
|
||||||
|
"paths": { "*": ["js/*"] },
|
||||||
|
}
|
||||||
|
}
|
42
webpack.config.js
Normal file
42
webpack.config.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
const path = require('path');
|
||||||
|
const process = require('process');
|
||||||
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: process.env.NODE_ENV || "development",
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
entry: {
|
||||||
|
index: './src/js/index.tsx',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: 'js/[name].js',
|
||||||
|
path: path.resolve(__dirname, 'build')
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
roots: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||||
|
modules: [path.resolve('src/js'), path.resolve('node_modules')],
|
||||||
|
extensions: ['.ts', '.tsx']
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{ from: "src/html/", to: "html" },
|
||||||
|
{ from: "src/css/", to: "css" },
|
||||||
|
{ from: "src/manifest.json", to: "manifest.json" },
|
||||||
|
{ from: "src/images", to: "images/" },
|
||||||
|
{ from: "src/_locales", to: "_locales/" }
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
],
|
||||||
|
}
|
Loading…
Reference in a new issue