Bagaimana cara memuat ulang file secara otomatis di Node.js?


444

Adakah ide tentang bagaimana saya bisa menerapkan auto-reload file di Node.js? Saya bosan me-restart server setiap kali saya mengubah file. Rupanya require()fungsi Node.js tidak memuat ulang file jika sudah diperlukan, jadi saya perlu melakukan sesuatu seperti ini:

var sys     = require('sys'), 
    http    = require('http'),
    posix   = require('posix'),
    json    = require('./json');

var script_name = '/some/path/to/app.js';
this.app = require('./app').app;

process.watchFile(script_name, function(curr, prev){
    posix.cat(script_name).addCallback(function(content){
        process.compile( content, script_name );
    });
});

http.createServer(this.app).listen( 8080 );

Dan di file app.js yang saya miliki:

var file = require('./file');
this.app = function(req, res) { 
    file.serveFile( req, res, 'file.js');  
}

Tapi ini juga tidak berfungsi - saya mendapatkan kesalahan dalam process.compile()pernyataan yang mengatakan bahwa 'memerlukan' tidak didefinisikan. process.compilesedang menghindari app.js , tetapi tidak memiliki petunjuk tentang node.js global.


4
Anda tahu Anda bisa menjalankan kode ini pada setiap permintaan:Object.keys(require.cache).forEach(function(key) { delete require.cache[key]; });
Tower

Jawaban:


562

Alternatif yang baik dan terkini supervisoradalah nodemon:

Monitor untuk setiap perubahan dalam aplikasi node.js Anda dan secara otomatis restart server - sempurna untuk pengembangan

Untuk menggunakan nodemon:

$ npm install nodemon -g
$ nodemon app.js

2
dan jika Anda ingin menggunakannya dalam Nitrous.io - $ nodemon -L yourfile.js(penjelasan lengkap di coderwall.com/p/aqxl_q )
drzaus

3
Tetapi dalam kasus ini, itu me-restart proses server juga.
Filipe

@Filipe, Anda benar. Saya diarahkan untuk masuk kembali. Saya berharap itu hanya memuat modul modifikasi tertentu.
ar2015

7
automatically restart the server - perfect for developmentterlalu banyak hiperbola. Muat ulang server bisa berarti masuk ke layanan backend yang membutuhkan waktu lama dalam kasus saya. "Sempurna untuk pengembangan" akan menjadi sesuatu seperti kelas reload panas saat proses berjalan dalam memori tanpa kehilangan status apa yang dilakukan android studio ketika Anda mengubah kode sumber.
nurettin

2
gunakan npm install [--save-dev | -D] nodemonuntuk membatasi instalasi untuk ruang lingkup proyek.
themefield

312

simpul-supervisor mengagumkan

penggunaan untuk memulai kembali pada save:

npm pasang supervisor -g
pengawas app.js

oleh isaacs - http://github.com/isaacs/node-supervisor


3
npm install -g supervisor. Ini harus diinstal secara global.
Kamal Reddy

Pada OSx 10.2.8 saya harus menjalankannya dengan sudo
Timopheym

2
Harus menjalankannya seperti ini di Windows:"C:\Program Files\nodejs\node.exe" C:\Users\Mark\AppData\Roaming\npm\node_modules\supervisor\lib\cli-wrapper.js app.js
mpen

1
tanpa -g atau sudo pada akar aplikasi: npm install supervisor, node node_modules/supervisor/lib/cli-wrapper.js app.js(saya memiliki instalasi non-akar Node)
h-Kippo

1
@ Markus Ini berarti simpul tidak ada di dalam AndaPATH
Blaise

87

saya menemukan cara sederhana:

delete require.cache['/home/shimin/test2.js']

7
Ini bagus jika Anda ingin memuat ulang perpustakaan eksternal tanpa memulai ulang aplikasi - dalam kasus saya, bot IRC.
Michelle Tilley

Ini luar biasa! Sangat sederhana dan bekerja dengan sangat baik. Setiap kali ada permintaan masuk, saya hanya menghapus file yang tidak tahan.
vaughan

16
delete require.cache[require.resolve('./mymodule.js')]; selesaikan kesepakatan dengan jalur nyata
Eduardo

Apakah ini aman untuk dilakukan atau dianggap "praktik buruk" atau "hanya pengembangan"?
jocull

2
@ jocull Saya tidak berpikir itu aman, karena dapat membuat ulang kelas dan fungsi atau ekspor apa pun, menghasilkan referensi yang berbeda ketika membandingkan dengan===
Kroltan

20

Jika seseorang masih datang ke pertanyaan ini dan ingin menyelesaikannya hanya dengan menggunakan modul standar, saya membuat contoh sederhana:

var process = require('process');
var cp = require('child_process');
var fs = require('fs');

var server = cp.fork('server.js');
console.log('Server started');

fs.watchFile('server.js', function (event, filename) {
    server.kill();
    console.log('Server stopped');
    server = cp.fork('server.js');
    console.log('Server started');
});

process.on('SIGINT', function () {
    server.kill();
    fs.unwatchFile('server.js');
    process.exit();
});

Contoh ini hanya untuk satu file (server.js), tetapi dapat diadaptasi ke beberapa file menggunakan array file, loop untuk mendapatkan semua nama file, atau dengan menonton direktori:

fs.watch('./', function (event, filename) { // sub directory changes are not seen
    console.log(`restart server`);
    server.kill();
    server = cp.fork('server.js');    
})

Kode ini dibuat untuk Node.js 0.8 API, tidak diadaptasi untuk beberapa kebutuhan spesifik tetapi akan berfungsi di beberapa aplikasi sederhana.

UPDATE: Fungsi ini diimplementasikan dalam modul simpleR saya , GitHub repo


1
Ini adalah solusi yang bagus dan sederhana. Saya hanya menggunakannya untuk bot yang seharusnya memperbarui sendiri dari git ketika diberitahu oleh moderator. Masalahnya adalah begitu Anda berada di dalam aplikasi, Anda tidak dapat memulai ulang sendiri. Namun, saya dapat menggunakan metode Anda untuk menelurkan instance bot dan menonton dotfile. Bot kemudian memperbarui dirinya sendiri, menyentuh dotfile, dan akan secara otomatis dimulai kembali oleh peluncur. Luar biasa!
Fred

@ Fred Saya senang mendengar ini :) Saya akan mengimplementasikan solusi ini dalam sebuah modul, segera saya kira, saya punya beberapa ide lagi bagaimana memperluas fungsionalitasnya
micnic

Jika file watchtidak diperlukan, memuat ulang dapat dilakukan tanpa fs, dengan mendengarkan sinyal yang berbeda.
Vladimir Vukanac

18

nodemon muncul pertama kali dalam pencarian google, dan tampaknya melakukan trik:

npm install nodemon -g
cd whatever_dir_holds_my_app
nodemon app.js

8

Ada Node-Supervisor yang dapat Anda instal

npm install supervisor

lihat http://github.com/isaacs/node-supervisor


2
Lebih lanjut tentang me-restart server jika crash. simpul-supervisor juga me-restart seluruh proses ketika file yang ditonton telah diubah. Ini bukan hot-reload dalam arti yang ketat.
nalply

Meskipun tidak benar-benar hot-loading, alat ini sangat berguna jika Anda hanya ingin kode untuk autoreload saat Anda sedang mengembangkan sehingga Anda tidak perlu me-restart node di baris perintah setelah setiap perubahan.
Derek Dahmer

7

Sunting: Jawaban saya sudah usang. Node.js adalah teknologi yang berubah sangat cepat.

Saya juga bertanya-tanya tentang memuat ulang modul. Saya memodifikasi node.js dan telah mempublikasikan sumbernya di Github di nalply / node . Satu-satunya perbedaan adalah fungsi require. Ini memiliki argumen kedua opsional reload.

require(url, reload)

Untuk memuat ulang app.jsdalam penggunaan direktori saat ini

app = require("./app", true);

Tulis sesuatu seperti ini, dan Anda memiliki auto- reload:

process.watchFile(script_name, function(curr, prev) {
    module = reload(script_name, true);
});

Satu-satunya masalah yang saya lihat adalah variabel module, tetapi saya sedang mengerjakannya sekarang.


7

nodemonadalah yang hebat. Saya hanya menambahkan lebih banyak parameter untuk opsi debugging dan menonton.

package.json

  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon --watch server --inspect ./server/server.js"
  }

Perintah: nodemon --watch server --inspect ./server/server.js

Sedangkan:

--watch serverRestart aplikasi ketika mengubah .js, .mjs, .coffee, .litcoffee, dan .jsonfile dalam serverfolder (termasuk subfolder).

--inspect Aktifkan debug jarak jauh.

./server/server.js Titik masuk.

Kemudian tambahkan konfigurasi berikut ke launch.json(Kode VS) dan mulai debugging kapan saja.

{
    "type": "node",
    "request": "attach",
    "name": "Attach",
    "protocol": "inspector",
    "port": 9229
}

Perhatikan bahwa lebih baik untuk menginstal nodemonsebagai dependensi dev proyek. Jadi anggota tim Anda tidak perlu menginstalnya atau mengingat argumen perintah, mereka hanya npm run devdan mulai meretas.

Lihat lebih lanjut tentang nodemondokumen: https://github.com/remy/nodemon#monitoring-multiple-directories


Globbing tidak didukung untuk versi nodemon terbaru (setidaknya 1.19.0). Cukup gunakan nodemon --watch server --inspect ./server/server.js sebagai gantinya.
Alex

Terima kasih @Alex atas informasi Anda. Diperbarui jawabannya.
Ninh Pham

5

Ada utas terbaru tentang subjek ini di milis node.js. Jawaban singkatnya adalah tidak, saat ini tidak mungkin memuat ulang secara otomatis file yang diperlukan, tetapi beberapa orang telah mengembangkan tambalan yang menambahkan fitur ini.


1
+1 Ya. Saya berpartisipasi dalam diskusi. Saya mengakui bahwa solusi saya terlalu sederhana. Ini hanya berfungsi jika modul panas itu sendiri tidak memerlukan modul lebih lanjut. Solusi Felix lebih dipikirkan dengan matang tetapi diperdebatkan jika auto- reload benar-benar milik inti.
nalply

5

solusi lain untuk masalah ini adalah menggunakan selamanya

Kemampuan lain yang berguna dari Forever adalah bahwa ia dapat me-restart aplikasi Anda ketika file sumber telah berubah. Ini membebaskan Anda dari keharusan me-restart secara manual setiap kali Anda menambahkan fitur atau memperbaiki bug. Untuk memulai Forever dalam mode ini, gunakan flag -w:

forever -w start server.js

Anehnya dengan flag -w aplikasi express.js saya tidak menggunakan CSS.
Costa

5

simpul-dev berfungsi dengan baik. npminstall node-dev

Bahkan memberikan pemberitahuan desktop ketika server dimuat ulang dan akan memberikan keberhasilan atau kesalahan pada pesan.

mulai aplikasi Anda pada baris perintah dengan:

node-dev app.js


3

Berikut adalah posting blog tentang Hot Reloading for Node. Ini menyediakan cabang Node github yang dapat Anda gunakan untuk mengganti instalasi Node Anda untuk mengaktifkan Hot Reloading.

Dari blog:

var requestHandler = require('./myRequestHandler');

process.watchFile('./myRequestHandler', function () {
  module.unCacheModule('./myRequestHandler');
  requestHandler = require('./myRequestHandler');
}

var reqHandlerClosure = function (req, res) {
  requestHandler.handle(req, res);
}

http.createServer(reqHandlerClosure).listen(8000);

Sekarang, setiap kali Anda memodifikasi myRequestHandler.js, kode di atas akan melihat dan mengganti requestHandler lokal dengan kode baru. Setiap permintaan yang ada akan terus menggunakan kode lama, sementara setiap permintaan masuk baru akan menggunakan kode baru. Semua tanpa mematikan server, memantulkan permintaan, membunuh permintaan sebelum waktunya, atau bahkan mengandalkan penyeimbang beban yang cerdas.


Satu-satunya hal dengan solusi ini adalah bahwa itu adalah garpu dari versi Node yang lebih lama, sehingga harus diubah dan digabungkan dengan versi terbaru sebelum menggunakan (kecuali Anda tidak keberatan menggunakan versi Node yang lebih lama).
Chetan

3

Saya sedang bekerja membuat simpul kecil "hal" yang dapat memuat / membongkar modul sesuka hati (jadi, yaitu Anda bisa dapat me-restart bagian dari aplikasi Anda tanpa membawa seluruh aplikasi ke bawah). Saya menggabungkan manajemen ketergantungan (sangat bodoh), sehingga jika Anda ingin menghentikan modul, semua modul yang bergantung pada itu akan dihentikan juga.

Sejauh ini bagus, tapi kemudian saya menemukan masalah tentang cara memuat ulang modul. Rupanya, seseorang hanya dapat menghapus modul dari cache "memerlukan" dan menyelesaikan pekerjaan. Karena aku tidak tertarik untuk mengubah langsung kode sumber node, saya datang dengan sangat Hacky-hack yaitu: pencarian di jejak stack panggilan terakhir untuk "membutuhkan" fungsi, ambil referensi ke lapangan "cache" itu dan..ya, hapus referensi ke simpul:

    var args = arguments
    while(!args['1'] || !args['1'].cache) {
        args = args.callee.caller.arguments
    }
    var cache = args['1'].cache
    util.log('remove cache ' + moduleFullpathAndExt)
    delete( cache[ moduleFullpathAndExt ] )

Lebih mudah, sebenarnya:

var deleteCache = function(moduleFullpathAndExt) {
  delete( require.cache[ moduleFullpathAndExt ] )
}

Rupanya, ini berfungsi dengan baik. Saya sama sekali tidak tahu apa arti argumen itu ["1"], tetapi ia melakukan tugasnya. Saya percaya bahwa orang-orang node akan menerapkan fasilitas reload suatu hari nanti, jadi saya kira untuk saat ini solusi ini dapat diterima juga. (btw. "benda" saya akan ada di sini: https://github.com/cheng81/wire , pergi ke sana dalam beberapa minggu dan Anda akan melihat apa yang saya bicarakan)


.. tentu saja tidak sesederhana itu. Itu hanya berfungsi jika ada panggilan yang diperlukan dalam tumpukan panggilan. Oh well, hack mudah di atas hack: menulis hal-hal itu dalam skrip temp, dan memerlukannya saat runtime. Apakah itu berhasil, dan bahkan membersihkan dirinya sendiri dari cache
cheng81

Dan sebenarnya itu lebih mudah: delete (require.cache [moduleFullpathAndExt])
cheng81

Modul Node.js sebenarnya dibungkus dengan fungsi anonim yang merupakan cara enkapsulasi modul dilakukan. Setiap modul sebenarnya terlihat seperti function (module, require) { /* your code */ }. Ketika Anda mempertimbangkan ini, arguments[1]menunjuk pada require. Dan loop sementara ada untuk situasi di mana Anda menyebutnya dari dalam fungsi lain dalam modul (itu hanya naik hirarki fungsi dan memeriksa nilai argumen yang diteruskan ke masing-masing).
JK

3

Anda dapat menggunakan nodemon dari NPM . Dan jika Anda menggunakan generator Express maka Anda dapat menggunakan perintah ini di dalam folder proyek Anda:

nodemon npm start

atau menggunakan mode Debug

DEBUG=yourapp:* nodemon npm start

Anda juga bisa langsung berlari

nodemon your-app-file.js

Semoga bantuan ini.


1

solusi di: http://github.com/shimondoodkin/node-hot-reload

perhatikan bahwa Anda harus mengurus sendiri referensi yang digunakan.

itu berarti jika Anda melakukannya: var x = membutuhkan ('foo'); y = x; z = x.bar; dan panas memuatnya kembali.

itu berarti Anda harus mengganti referensi yang disimpan dalam x, y dan z. dalam fungsi panggil balik hot reaload.

beberapa orang bingung hot reload dengan auto restart modul nodejs-autorestart saya juga memiliki integrasi pemula untuk mengaktifkan auto start saat boot. jika Anda memiliki aplikasi kecil restart otomatis baik-baik saja, tetapi ketika Anda memiliki aplikasi besar, hot reload lebih cocok. hanya karena hot reload lebih cepat.

Saya juga suka modul node-inflow saya.


1

Tidak perlu menggunakan nodemon atau alat lain seperti itu. Cukup gunakan kemampuan IDE Anda.

Mungkin salah satu yang terbaik adalah IntelliJ WebStorm dengan fitur isi ulang panas (server otomatis dan peramban ulang) untuk node.js .


1

Berikut adalah metode berteknologi rendah untuk digunakan di Windows. Masukkan ini dalam file batch yang disebut serve.bat:

@echo off

:serve
start /wait node.exe %*
goto :serve

Sekarang alih-alih lari node app.jsdari shell cmd Anda, jalankan serve app.js.

Ini akan membuka jendela shell baru yang menjalankan server. File batch akan memblokir (karena /wait) sampai Anda menutup jendela shell, pada titik mana shell cmd asli akan bertanya "Hentikan pekerjaan batch (Y / T)?" Jika Anda menjawab "N" maka server akan diluncurkan kembali.

Setiap kali Anda ingin me-restart server, tutup jendela server dan jawab "N" di cmd shell.


1

struktur aplikasi saya:

NodeAPP (folder)
   |-- app (folder)
      |-- all other file is here
   |-- node_modules (folder)
   |-- package.json
   |-- server.js (my server file)

instal ulang terlebih dahulu dengan perintah ini:

npm install [-g] [--save-dev] reload

lalu ubah package.json :

"scripts": {
    "start": "nodemon -e css,ejs,js,json --watch app"
}

sekarang Anda harus menggunakan memuat ulang di file server Anda :

var express = require('express');
var reload = require('reload');
var app = express();

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() {
    console.log( 'server is running on port ' + app.get('port'));
});

reload(server, app);

dan untuk perubahan terakhir, akhir tanggapan Anda kirim skrip ini :

<script src="/reload/reload.js"></script>

sekarang mulai aplikasi Anda dengan kode ini:

npm start

Namun, pendekatan ini tidak berhasil, yang mengarah pada npmjs.com/package/reload (untuk aplikasi Express).
Maxie Berkmann

0

Gunakan ini:

function reload_config(file) {
  if (!(this instanceof reload_config))
    return new reload_config(file);
  var self = this;

  self.path = path.resolve(file);

  fs.watchFile(file, function(curr, prev) {
    delete require.cache[self.path];
    _.extend(self, require(file));
  });

  _.extend(self, require(file));
}

Yang harus Anda lakukan sekarang adalah:

var config = reload_config("./config");

Dan config akan secara otomatis dimuat ulang :)


Punya versi yang tidak mengandalkan kerangka yang bukan bagian dari Node?
Adrian

0

loaddir adalah solusi saya untuk memuat cepat suatu direktori, secara rekursif.

dapat kembali

{ 'path/to/file': 'fileContents...' } atau { path: { to: { file: 'fileContents'} } }

Ini memiliki callbackyang akan dipanggil ketika file diubah.

Ini menangani situasi di mana file cukup besar yang watchdipanggil sebelum mereka selesai menulis.

Saya telah menggunakannya dalam proyek selama sekitar satu tahun, dan baru-baru ini menambahkan janji untuk itu.

Bantu aku pertempuran mengujinya!

https://github.com/danschumann/loaddir


0

Anda dapat menggunakan auto-reload untuk memuat kembali modul tanpa mematikan server.

Install

npm install auto-reload

contoh

data.json

{ "name" : "Alan" }

test.js

var fs = require('fs');
var reload = require('auto-reload');
var data = reload('./data', 3000); // reload every 3 secs

// print data every sec
setInterval(function() {
    console.log(data);
}, 1000);

// update data.json every 3 secs
setInterval(function() {
    var data = '{ "name":"' + Math.random() + '" }';
    fs.writeFile('./data.json', data);
}, 3000);

Hasil:

{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: 'Alan' }
{ name: '0.8272748321760446' }
{ name: '0.8272748321760446' }
{ name: '0.8272748321760446' }
{ name: '0.07935990858823061' }
{ name: '0.07935990858823061' }
{ name: '0.07935990858823061' }
{ name: '0.20851597073487937' }
{ name: '0.20851597073487937' }
{ name: '0.20851597073487937' }

0

solusi sederhana lain adalah dengan menggunakan fs.readFile daripada menggunakan membutuhkan Anda dapat menyimpan file teks yang berisi objek json, dan membuat interval pada server untuk memuat ulang objek ini.

pro:

  • tidak perlu menggunakan lib eksternal
  • relevan untuk produksi (memuat ulang file konfigurasi saat perubahan)
  • mudah diimplementasikan

kontra:

  • Anda tidak dapat memuat ulang modul - hanya json yang berisi data nilai kunci

0

Bagi orang yang menggunakan Vagrant dan PHPStorm, pengamat file adalah pendekatan yang lebih cepat

  • nonaktifkan sinkronisasi langsung file sehingga Anda menjalankan perintah hanya pada save lalu buat ruang lingkup untuk file * .js dan direktori kerja dan tambahkan perintah ini

    vagrant ssh -c "/var/www/gadelkareem.com/forever.sh restart"

dimana selamanya. sh itu seperti

#!/bin/bash

cd /var/www/gadelkareem.com/ && forever $1 -l /var/www/gadelkareem.com/.tmp/log/forever.log -a app.js

0

Saya baru-baru ini datang ke pertanyaan ini karena tersangka tidak bekerja dengan paket tertaut. Jika Anda seperti saya dan memanfaatkan npm linkselama pengembangan untuk bekerja secara efektif pada proyek yang terdiri dari banyak paket, penting bahwa perubahan yang terjadi pada dependensi memicu pemuatan ulang juga.

Setelah mencoba node-mon dan pm2, bahkan mengikuti instruksi mereka untuk tambahan menonton folder node_modules, mereka masih tidak menerima perubahan. Meskipun ada beberapa solusi khusus dalam jawaban di sini, untuk hal seperti ini, paket terpisah lebih bersih. Saya menemukan node-dev hari ini dan berfungsi dengan baik tanpa opsi atau konfigurasi.

Dari Readme:

Berbeda dengan alat-alat seperti pengawas atau nodemon itu tidak memindai sistem file untuk file yang akan ditonton. Alih-alih itu menghubungkan ke fungsi Node yang membutuhkan () untuk menonton hanya file yang benar-benar diperlukan.


0
const cleanCache = (moduleId) => {
    const module = require.cache[moduleId];
    if (!module) {
        return;
    }
    // 1. clean parent
    if (module.parent) {
        module.parent.children.splice(module.parent.children.indexOf(module), 1);
    }
    // 2. clean self
    require.cache[moduleId] = null;
};

0

Anda dapat melakukannya dengan menyegarkan browser . Aplikasi simpul Anda dimulai ulang secara otomatis, halaman hasil Anda di browser juga diperbarui secara otomatis. Kelemahannya adalah Anda harus meletakkan cuplikan js di laman yang dibuat. Inilah repo untuk contoh kerja.

const http = require('http');
const hostname = 'localhost';
const port = 3000;

const server = http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html; charset=UTF-8');
    res.write('Simple refresh!');
    res.write(`<script src=${process.env.BROWSER_REFRESH_URL}></script>`);
    res.end();
})

server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}/`);

    if (process.send) {
        process.send({ event: 'online', url: `http://${hostname}:${port}/` })
    }

});

0

Saya sudah mencoba PM2 : instalasi juga mudah dan mudah digunakan; hasilnya memuaskan. Namun, kita harus mengurus edisi PM2 mana yang kita inginkan. runtime pm 2 adalah edisi gratis, sedangkan perusahaan pm2 plus dan pm2 tidak gratis.

Sedangkan untuk Strongloop , instalasi saya gagal atau tidak lengkap, jadi saya tidak bisa menggunakannya.


-1

Saat ini server WebPack dev dengan opsi panas digunakan. Anda dapat menambahkan skrip seperti ini di package.json Anda:"hot": "cross-env NODE_ENV=development webpack-dev-server --hot --inline --watch-poll",

dan setiap perubahan pada file Anda akan memicu kompilasi ulang secara otomatis


2
Jawaban ini salah dengan pertanyaan. Webpack untuk aplikasi frontend dan server dev adalah server web sendiri. Pertanyaan itu merujuk pada aplikasi server yang diimplementasikan di Node. Tidak perlu server web. Sudah satu.
DanielKhan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.