This commit is contained in:
许彦峰
2019-03-15 10:08:39 +08:00
commit e7adf25ccf
75 changed files with 25883 additions and 0 deletions

18
template/.babelrc Normal file
View File

@@ -0,0 +1,18 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["istanbul"]
}
}
}

3
template/.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
core/*.js
core/*/*.js
build/*.js

31
template/.eslintrc.js Normal file
View File

@@ -0,0 +1,31 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
globals: {
"chrome": true
// chrome: true
},
env: {
browser: true,
},
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}

12
template/.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
# IDE
.vscode
# dependencies
node_modules
# logs
npm-debug.log
yarn.lock
# Backpack build
build

8
template/.postcssrc.js Normal file
View File

@@ -0,0 +1,8 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

11
template/core/page.ejs Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
</head>
<body>
<div id="root"></div>
</body>
</html>

60
template/core/tools.js Normal file
View File

@@ -0,0 +1,60 @@
const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
exports.htmlPage = (title, filename, chunks, template) => new HtmlWebpackPlugin({
title,
hash: true,
cache: true,
inject: 'body',
filename: './pages/' + filename + '.html',
template: template || path.resolve(__dirname, './page.ejs'),
appMountId: 'app',
chunks
})
exports.cssLoaders = (options = {}) => {
let loaders = {}
let prePprocessors = {
css: {},
postcss: {},
less: { loader: 'less'},
sass: { loader:'sass', options: { indentedSyntax: true } },
scss: { loader:'sass' },
stylus: { loader: 'stylus' },
styl: { loader: 'stylus' }
}
for(let key in prePprocessors) {
let loader = [{
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production'
}
}]
if (prePprocessors[key].loader) {
loader.push({
loader: prePprocessors[key].loader + '-loader',
options: Object.assign({}, prePprocessors[key].options, { sourceMap: options.sourceMap })
})
}
if (options.extract) {
loaders[key] = ExtractTextPlugin.extract({ use: loader, fallback: 'vue-style-loader' })
} else {
loaders[key] = ['vue-style-loader'].concat(loader)
}
}
return loaders;
}
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

View File

@@ -0,0 +1,106 @@
const path = require('path')
const webpack = require('webpack')
const ChromeReloadPlugin = require('wcer')
const {cssLoaders, htmlPage} = require('./tools')
const CopyWebpackPlugin = require('copy-webpack-plugin')
let resolve = dir => path.join(__dirname, '..', 'src', dir)
module.exports = {
entry: {
tab: resolve('./tab'),
popup: resolve('./popup'),
options: resolve('./options'),
content: resolve('./content'),
devtools: resolve('./devtools'),
background: resolve('./backend'),
panel: resolve('./devtools/panel'),
inject: resolve('./content/inject'),
},
output: {
path: path.join(__dirname, '..', 'build'),
publicPath: '/',
filename: 'js/[name].js',
chunkFilename: 'js/[id].[name].js?[hash]',
library: '[name]'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [path.join(__dirname, '..', 'src'), path.join(__dirname, '..', 'test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
...cssLoaders(),
js: { loader: 'babel-loader' }
},
transformToRequire: {
video: 'src',
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.join(__dirname, '..', 'src'), path.join(__dirname, '..', 'test')],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:7].[ext]'
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[hash:7].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[hash:7].[ext]'
}
}
]
},
plugins: [
htmlPage('home', 'app', ['tab']),
htmlPage('popup', 'popup', ['popup']),
htmlPage('panel', 'panel', ['panel']),
htmlPage('devtools', 'devtools', ['devtools']),
htmlPage('options', 'options', ['options']),
htmlPage('background', 'background', ['background']),
new CopyWebpackPlugin([{ from: path.join(__dirname, '..', 'static') }]),
new ChromeReloadPlugin({
port: 9090,
manifest: path.join(__dirname, '..', 'src', 'manifest.js')
}),
],
performance: { hints: false },
}

View File

@@ -0,0 +1,21 @@
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpack = require('./webpack.base')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const {styleLoaders} = require('./tools')
module.exports = merge(baseWebpack, {
// cheap-module-eval-source-map быстрее для разработки
watch: true,
module: {
rules: styleLoaders({ sourceMap: false })
},
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"development"'
}),
new FriendlyErrorsPlugin()
]
})

View File

@@ -0,0 +1,47 @@
const fs = require('fs')
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const baseWebpack = require('./webpack.base')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const {styleLoaders} = require('./tools')
module.exports = merge(baseWebpack, {
devtool: '#cheap-module-eval-source-map',
module: {
rules: styleLoaders({ extract: true, sourceMap: true })
},
plugins: [
new CleanWebpackPlugin(['build/*.*']),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
new ExtractTextPlugin({
filename: 'css/[name].[contenthash].css'
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module) {
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
]
})

13295
template/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

69
template/package.json Normal file
View File

@@ -0,0 +1,69 @@
{
"name": "vue-chrome-extension-template",
"version": "0.0.1",
"description": "vue.js chrome extension template (wcer)",
"author": "works.yura@gmail.com",
"license": "MIT",
"private": false,
"dependencies": {
"axios": "^0.17.0",
"element-ui": "^2.0.1",
"lodash": "^4.17.4",
"vue": "^2.5.2",
"vue-meta": "^1.2.0",
"vue-pouch": "^0.0.22",
"vue-router": "^3.0.1",
"vuex": "^3.0.0"
},
"scripts": {
"lint": "eslint --ext .js,.vue src",
"dev": "webpack --config ./core/webpack.dev.js --hide-modules",
"build": "webpack --config ./core/webpack.prod.js -p --progress --hide-modules --colors"
},
"devDependencies": {
"archiver": "^2.1.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.1",
"babel-loader": "^7.1.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-2": "^6.24.1",
"babel-register": "^6.26.0",
"buble": "^0.16.0",
"buble-loader": "^0.4.1",
"clean-webpack-plugin": "^0.1.17",
"copy-webpack-plugin": "^4.2.0",
"cross-env": "^5.1.0",
"css-loader": "^0.28.7",
"enhanced-resolve": "^3.4.1",
"eslint": "^4.9.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.9.0",
"eslint-plugin-html": "^3.2.2",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue-libs": "^1.2.1",
"extract-text-webpack-plugin": "^3.0.1",
"file-loader": "^1.1.5",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-sass": "^4.5.3",
"nodemon": "^1.12.1",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"pug": "^2.0.0-rc.4",
"pug-loader": "^2.3.0",
"sass-loader": "^6.0.6",
"url-loader": "^0.6.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.3",
"vue-template-compiler": "^2.5.2",
"wcer": "^1.0.2",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.3",
"webpack-merge": "^4.1.0",
"ws": "^3.2.0"
}
}

View File

@@ -0,0 +1 @@
console.log('background !')

View File

@@ -0,0 +1 @@
console.log('content-script!')

View File

@@ -0,0 +1,5 @@
var content = chrome.extension.getURL('js/content.js')
var script = document.createElement('script')
script.setAttribute('type', 'text/javascript')
script.setAttribute('src', content)
document.body.appendChild(script)

View File

@@ -0,0 +1,3 @@
chrome.devtools.panels.create('panel', 'img/logo.png', 'pages/panel.html', function (panel) {
console.log('hello from callback')
})

View File

@@ -0,0 +1,8 @@
import Vue from 'vue'
import root from './root.vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#root',
render: h => h(root)
})

View File

@@ -0,0 +1,18 @@
<template lang="pug">
div devtools
</template>
<script>
export default {
data: () => ({
}),
computed: { },
created () { },
mounted () { },
methods: { }
}
</script>
<style lang="scss">
div {
color: blue
}
</style>

View File

@@ -0,0 +1,22 @@
export default {
get (key) {
try {
return JSON.parse(localStorage.getItem(key))
} catch (e) {}
},
set (key, val) {
try {
localStorage.setItem(key, JSON.stringify(val))
} catch (e) {}
},
remove (key) {
try {
localStorage.removeItem(key)
} catch (e) {}
},
clear () {
try {
localStorage.clear()
} catch (e) {}
}
}

41
template/src/manifest.js Normal file
View File

@@ -0,0 +1,41 @@
module.exports = {
name: 'Vue Extension',
version: '1.0.0',
description: 'Vue.js Chrome Extension Template (wcer)',
author: 'yura',
manifest_version: 2,
icons: { '16': 'icons/16.png', '128': 'icons/128.png' },
permissions: [
'<all_urls>',
'*://*/*',
'activeTab',
'tabs',
'cookies',
'background',
'contextMenus',
'unlimitedStorage',
'storage',
'notifications',
'identity',
'identity.email'
],
browser_action: {
default_title: 'title',
default_popup: 'pages/popup.html'
},
background: {
persistent: false,
page: 'pages/background.html'
},
devtools_page: 'pages/devtools.html',
options_page: 'pages/options.html',
content_scripts: [{
js: [ 'js/inject.js' ],
run_at: 'document_end',
matches: ['<all_urls>'],
all_frames: true
}],
content_security_policy: "script-src 'self' 'unsafe-eval'; object-src 'self'",
web_accessible_resources: [ 'panel.html', 'js/content.js' ]
}

View File

@@ -0,0 +1,8 @@
import Vue from 'vue'
import root from './root.vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#root',
render: h => h(root)
})

View File

@@ -0,0 +1,18 @@
<template lang="pug">
div options
</template>
<script>
export default {
data: () => ({
}),
computed: { },
created () { },
mounted () { },
methods: { }
}
</script>
<style>
div {
color: blue
}
</style>

View File

@@ -0,0 +1,12 @@
import Vue from 'vue'
import root from './root.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#root',
render: h => h(root)
})

View File

@@ -0,0 +1,23 @@
<template lang="pug">
div
el-button(type="primary" @click="tab") New tab
</template>
<script>
export default {
data: () => ({
}),
computed: { },
created () { },
mounted () { },
methods: {
tab () {
chrome.tabs.create({ url: 'pages/app.html' })
}
}
}
</script>
<style lang="scss">
div {
color: blue
}
</style>

View File

@@ -0,0 +1,8 @@
import Vue from 'vue'
import root from './root.vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#root',
render: h => h(root)
})

18
template/src/tab/root.vue Normal file
View File

@@ -0,0 +1,18 @@
<template lang="pug">
div tab
</template>
<script>
export default {
data: () => ({
}),
computed: { },
created () { },
mounted () { },
methods: { }
}
</script>
<style lang="scss">
div {
color: blue
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB