Google Chrome naršyklės plėtinių rašymas

Aptarsime šiuos punktus.
- Nuo ko pradėti
- Pagrindiniai principai
- Darbo stalas
- Testavimas
- Įrankiai
- Darome kažką mandro
- Automatizavimas
Bonusai
Nuo ko pradėti
Pirmiausia reikia susigalvoti ką darysime. Imkime patį papraščiausią variantą kurį nesunkiai galėtumėme realizuoti ir be išssidirbinėjimų (jokių 3D žaidimų ar skraidančių makaronų). Kadangi mūsų pluginas bus primityvus - nenaudosime daugiakalbystės, specialių meniu ar nustatymų.
- Paruoškime naršyklę
Pagrindiniai principai
- Plugina rašysime JavaScript ES6. Kadangi naršykle dar pilnai nepalaiko ES6 mums prireiks transpiliatoriaus.
- Nenaudosime SCSS arba LESS - kadangi stilius bus minimalus.
- Įkeliati pluginą reikia
chrome://extensions/
puslapyje pasirinkti "Developer mode" ir paspausti mygtuką "Load unpacked extention..." - Po kiekvieno redagavimo mums prireiks naršyklėje
chrome://extensions/
puslapyje perkrauti pluginą.
Darbo stalas
Mums reikės:
- Node + npm Tai paketų tvarkyklė
- Yarn Greitesniam node programų diegimui. Tai paketų tvarkyklė
Juokinga, bet naudosim tris skirtingas paketų tvarkykles
Po sėkmingo šių programų diegimo musu projekto direktorijoje consolėje rašome komandas:
npm init
Tokiu būdu sukursime pagrindinipackage.json
failą.yarn add jspm --dev
Įdiegsime jspm Tai paketų tvarkyklėjspm init
Sukonfiguruokime paketų tvarkyklę ir transpiliatoriųEnter jspm packages folder
Nurodomesrc/lib
Enter config file path
Nurodome./lib/config.js
- Sukuriame
src
kataloga, jame laikysime savo scripto pagrindinius failus. Kodėl src? src
kataloge sukurkimemanifest.json
main.js
ircss/styles.css
failus. manifest.json Tai plugino pagrindinis failas. Jame aprašome visą informaciją apie pluginą, bei kokius failus užkrauti.
src/manifest.json
{
"name": "Mano super pluginas",
"version": "0.0.1",
"manifest_version": 2,
"homepage_url": "https://example.com",
"author": "Mano Vardas",
"description": "Mano plugino aprašymas",
"content_scripts": [
{
"matches": [
"*://github.com/*",
"*://gist.github.com/*"
],
"js": [
"main.js"
],
"css": [
"css/styles.css"
]
}
],
"icons": {
"16": "images/16x16.png",
"24": "images/24x24.png",
"32": "images/32x32.png",
"64": "images/64x64.png",
"128": "images/128x128.png"
}
}
src/main.js daugiau apie chrome API
class myPlugin {
constructor() {
// Išpjaunam pranešimą su plugino pavadinimu. Žinosim kad mūsų pluginas veikia
alert(chrome.runtime.getManifest().name);
}
}
new myPlugin();
src/css/styles.css
body {
/* Nustatom svetainės teksto spalvą į raudoną. Žinosim, kad veikia */
color: red !important;
}
- Atsisiųskime ikonėles pvz iš flaticon.com pasirinkdami atitinkamus dydžius
16x16, 24x24, 32x32, 64x64, 128x128
png formatu ir išsaugojame juos į./src/images/
direktoriją tokiais pavadinimais kaip nurodėmemanifest.json
faile. Kam to reikia? Toks pluginas atrodys profesionaliau. Jo ikonelės ant retina tipo ekranų neatrodys išplaukusios.
Mano manymu svarbiausios šio failo vietos yra: version ir content_scripts
version
: Tik keisdami versijos numeriuka priversite visus naudotojus atsinaujinti mūsų plugino versiją.content_scripts
: Skiltis matches nusako kuriose svetainėse bus įgalintas pluginas. Apie js ir css pakalbėsime vėliau- Savo programoje nustatykite JavaScript kalbos versija į ES6
Testavimas
Teoriškai mūsų naujasis pluginas jau turėtų veikti. Taigi testuojame. Einame į naršyklės plėtinių nustatymų puslapį chrome://extensions/
įsijungiame "Developer mode" ir spaudžiame "Load unpacked extention..." pasirenkame mūsų src
katalogą.
Iškarto po to keliaukime į http://github.com ir turėtumėme gauti
alert
pranešimą su mūsų plugino pavadinimu bei pakeista svetainės teksto spalva.
Įrankiai
Tai kuriem galam mums reikalingos papidlomos programos node, yarn, ISMP ir pan. Kam to reikia?
- procesas turėtų būti maximaliai automatizuotas.
- Tam reikalui naudojame
node
su beribėm galimybėm
- Tam reikalui naudojame
- Koda norime rašyti naudodami naujausias ES6 galimybes.
- Mums padės babel transpiliatorius
- Neturėtų būti vargas panaudoti kokią nors biblioteką pvz lodash
- Tai išsprendžia ISPM
- Plugino publikuotas kodas neturėtų būti lengvai perskaitomas, jei kas pamėgintų iškrapštyti source kodą.
- ISPM pagalba kodą minifikuosime.
Darome kažką mandro
Pameginkime panaudoti kokią nors biblioteką. Tai mūsų neapriboja, galime kad ir reactjs :)
jspm install npm:lodash
instaliuojame biblioteką iš NPM registro į JSPM biblioteką- Paredaguojame failą
src/main.js
import _ from 'lodash';
class myPlugin {
constructor() {
// Išpjaunam pranešimą su plugino pavadinimu. Žinosim kad mūsų pluginas veikia
alert(chrome.runtime.getManifest().name);
console.log(_.VERSION)
}
}
new myPlugin();
Ir Mūsų pluginas nebeveikia! : Uncaught SyntaxError: Unexpected token import
Kaip matome chrome dar nepalaiko import galimybės.
Pasitelkime JSPM
kodui sukompiliuoti. Terminale (konsolėje) rašome:
jspm bundle-sfx src/main
Turim gauti tokį pranešimą: ok Built into src/build.js with source maps, unminified.
Taigi buvo sukurtas naujas failas pavadinimu build.js
ji pakeisime vėliau. Tikriname ar veikia musu naujasis skriptas.
- Atsidarome
manifest.json
ir pakeičiamejs
reikšmę išmain.js
įbuild.js
kad patikrinti ar veikia mūsų naująsis failas.Po patikrinimo atstatykite
manifest.json
failejs
reikšmę įmain.js
- Perkrauname extention chrome naršyklėje.
Sėkmės atveju nuėje į github.com ir perkrovę naršykle atsidarę Dev Tools (F12 arba Ctrl + Shif + I) turim pamatyti lodash
versiją. Mano atveju ji buvo 4.16.5
Taigi ką mes čia gavome ir ką padarėme.
Pirmiausia atsidarykime build.js
failą ir pažiūrėkime. WOHOO - jame beveik 8000 kodo eilučių :D
Taigi - JSPM transpiliavo musų 11-a kodo eilučių į tokį monstrą. Kodėl jis toks didelis? Primenu, mes ką tik import
pagalba importavome nemažą biblioteką lodash
. Pabandykite ištrint importavimą ir konsolės išvedimo eilutę bei perkompiliuot jspm bundle-sfx main
. Turėtumeme gauti apie 26-as kodo eilutes.
Taip pat JSPM pagalba mes įdiegėme lodash
pluginą. Todėl mes galime kontroliuoti kuria plugino versija ėdiegti. Mums nebereikia tos bibliotekos atskirai saugotis ir jei naudojame GIT ar kitokia versijavimo sistemą - saugoti šios bibliotekos savo repozitorijoje.
Toks buildinimo būdas, kuris panaudoja babel
transpiliatorių, mums leidžia naudoti naujausios JavaScript kalbos versijos ES6 ir jei yra noras ES7 privalumus.
Automatizavimas
Mūsų tikslas, kad programa automatiškai atliktų šiuos veikmsus:
- Pakeistų mūsų plugino versiją laikantis SemVer principų
- Transpiliuotų mūsų JavascRipt kodo versiją iš ES6 į ES5
- Optimizuotų ir minifikuotų source kodą - šiek tiek apsaugant nuo smalsių akių
- Sugeneruotų chrome "extention" specialų
crx
irzip
archyvą Automatiškai sukeltų naujinimus į chrome extention developer puslapį
Šiem tikslam realizuoti pasitelksime node
privalumus.
Diegiam įrankius
yarn add semver --dev
Versijavimuiyarn add copyfiles --dev
failu kopijavimui- Diegiam
chrome-location
tačiau ji turi vieną problema - neatpažįstachromium
naršykles. Todėl diegiame forką iš kitos repozitorijos.yarn add https://github.com/CodersAKL/chrome-location.git#master --dev
Mums reikia žinoti kur guli musu chrome arba chromium naršyklė yarn add archiver --dev
Archyvavimo įrankisyarn add fs-extra --dev
Failų trinimuiyarn add pretty-bytes --dev
Tiesiog pasismaginimui. Atvaizduosime ZIP archyvo dydi žmoniškai :)yarn add sanitize-filename --dev
Saugiam failų pavadinimui generuoti.
Parašykime skriptus šiem įrankiam valdyt.
- Sukurkime katalogą
scripts
jame laikysime visus su pluginu nesusijusius javascriptus - Sukurkime failą
version.js
scripts/version.js
var path = require('path');
var version = require('semver');
var dist = path.resolve(__dirname + '/../dist');
var src = path.resolve(__dirname + '/../src');
var args = process.argv.splice(process.execArgv.length + 2);
var fs = require('fs');
var fileName = `${src}/manifest.json`;
var file = require(fileName);
// major, minor, patch, premajor, preminor, prepatch, or prerelease.
var release = args[0] || 'patch';
file.version = version.inc(file.version, release);
fs.writeFile(fileName, JSON.stringify(file, null, 2), function (err) {
if (err) return console.log(' ✘ ' + err);
console.log(` ✔ Released version: ${file.version}`);
});
Norint paleisti šį skriptą užtenka konsolėje parašyti node script/version.js
. Šis scriptas priima argumentus major, minor, patch, premajor, preminor, prepatch, prerelease.
Pagal nutylejima naudojamas patch
. Apie versijavimo parametrus galite pasiskaityti https://github.com/npm/node-semver
- Sukurkime failą
scripts/build.js
Jis paleis chrome naršyklės funkciją kuri sukurs
scripts/build.js
var chromeExe = require('chrome-location');
var fs = require("fs");
var path = require('path');
var fsex = require('fs-extra');
var archiver = require('archiver');
var prettyBytes = require('pretty-bytes');
var sanitize = require("sanitize-filename");
// http://nodejs.org/api.html#_child_processes
var exec = require('child_process').exec;
var root = path.resolve(__dirname + '/..');
var dist = path.resolve(__dirname + '/../dist');
// chromeExe --pack-extension=C:\myext --pack-extension-key=C:\myext.pem
var command = `${chromeExe} --pack-extension=${dist}`;
exec(command, function (err) {
if (err) {
return console.log(` ✘ ${err}`);
}
else {
var fileName = `${dist}/manifest.json`;
var extension = require(fileName);
var filename = sanitize(extension.name);
// Zip dir
var output = fs.createWriteStream(`${dist}/${filename}.zip`);
var archive = archiver.create('zip', {});
archive.pipe(output);
archive.bulk([
{
expand: true,
cwd: dist,
src: ["**/*.!(zip|crx|pem)"],
dot: false
}
]);
archive.on('error', function(err) { return console.log(` ✘ ${err}`); });
archive.finalize();
output.on('close', function() {
fsex.move(`${root}/dist.crx`, `${dist}/${filename}.crx`, function(err) {
if ( err ) {
return console.log(` ✘ ${err}`);
}
fsex.move(`${root}/dist.pem`, `${dist}/${filename}.pem`, function (err) {
if ( err ) {
return console.log(` ✘ ${err}`);
}
fsex.removeSync(`${dist}/**/*.!(zip|crx|pem)`);
console.log(` ✔ Build success. Released version:${extension.version} Zip size:${prettyBytes(archive.pointer())} Files: "dist/${filename}.crx" "dist/${filename}.zip"`);
});
});
});
}
});
Reikalingas mums dar skriptas kuris būtų atsakingas už failų valymą ir kopijavimą.
scripts/build.js
var fs = require("fs");
var path = require('path');
var fsex = require('fs-extra');
var sanitize = require("sanitize-filename");
// http://nodejs.org/api.html#_child_processes
var dist = path.resolve(__dirname + '/../dist');
var src = path.resolve(__dirname + '/../src');
// Clean dist directory, copy source files and remove unneeded files/dirs
fsex.emptyDirSync(dist);
// Copy source files
fsex.copySync(src, dist);
console.log(` ✔ Successfully copied to dist.`);
Nenorėčiau labai gilintis į šį kodą ir aiškinti ką čia prirašiau. Nenukrypkim nuo temos. Taigi crx
failą sugeneruoja naršyklė, tačiau ji neturi parametrų kurių pagalba būtų galima pakeisti direktoriją į kurią bus sukurti failai. Todel su šiuo skriptu mes tvarkome ne tik šią problemą bet ir dar keletas.
Taigi norint paleisti šį skriptą reikia konsolėje rašyti node scripts/build
. Tačiau mums reiktų šiektiek apsikuopti ir organizuoti šias komandas. Todėl pasinaudosime node scripts parametru esančiu package.json
faile.
Redaguojame package.json
failą
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"postinstall": "jspm install && npm run build",
"release": "node scripts/version",
"dev": "npm run build:copy && jspm bundle-sfx src/main dist/main.js",
"build": "npm run release && npm run build:copy && npm run build:compile && npm run build:finalize",
"build:copy": "node scripts/copy",
"build:compile": "NODE_ENV=production jspm bundle-sfx src/main --minify --skip-source-maps dist/main.js",
"build:finalize": "node scripts/build"
},
Taigi turime keletas komandų kurios mum palengvins gyvenimą.
npm run dev
Ši komanda nukopijuos source failus i dist
katalogą ir sukompiliuos su jspm
main.js
npm run build
Ši komanda padaro galutinį musų release versiją. Sukuria ZIP
ir CRX
failus
npm run release major
Pakeičia versijos numeriuką
Bonusai
- Galutinį šio spraipnelio kodą galite rasti GitHub'e
- Smagus extention chrome naršyklei https://chrome.google.com/webstore/detail/jifpbeccnghkjeaalbbjmodiffmgedin?utm_source=chrome-app-launcher-info-dialog Jo pagalba galėsite peržiūrėti source kodus kitų platinamų pluginų.
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}