/* eslint-env browser */ import os from 'os' import fs from 'fs' import path from 'path' import { spawn } from 'child_process' const { config, versions } = Pear const BIN = path.join(config.pearDir, 'bin') const isWin = process.platform === 'win32' customElements.define('pear-welcome', class extends HTMLElement { constructor () { super() this.template = document.createElement('template') this.template.innerHTML = `

Pear is in the system PATH and ready to go .

Welcome...

...to the Internet of Peers.

Pear provides the pear Command-line Interface as the primary interface for developing, sharing & maintaining unstoppable peer-to-peer applications and systems.

To get started, open a terminal, type pear and hit return.

` this.root = this.attachShadow({ mode: 'open' }) this.root.appendChild(this.template.content.cloneNode(true)) } connectedCallback () { versions().then(({ app, platform }) => { // this.root.querySelector('#platform-version').innerText = 'Pear version: ' + platform.key + '_' + platform.length // this.root.querySelector('#app-version').innerText = 'App version: ' + app.key + '_' + app.length }) } }) customElements.define('system-status', class extends HTMLElement { router = null connectedCallback () { this.root.addEventListener('click', (evt) => { this.router.link(evt) }) } load () { this.style.display = '' } unload () { this.style.display = 'none' } constructor () { super() this.zsh = false this.bash = false this.statement = `export PATH="${BIN}":$PATH` this.stmtrx = new RegExp(`^export PATH="${BIN}":\\$PATH$`, 'm') this.shellProfiles = null this.installed = this.#installed() this.template = document.createElement('template') this.template.innerHTML = `

System Status

${ this.installed ? '' : `

Pear setup is nearly complete.

Click the button to add ${BIN} to the system PATH

` }
` this.root = this.attachShadow({ mode: 'open' }) this.root.appendChild(this.template.content.cloneNode(true)) this.button = this.root.querySelector('button') if (this.button) { const listener = () => { this.button.removeEventListener('click', listener) this.#install() .then(() => { this.replaceWith(new this.constructor()) console.log('now show version info, and a gif showing pear command line help output run through') }) .catch((err) => this.#error(err)) } this.button.addEventListener('click', listener) } } #error (err) { console.error(err) } #installed () { if (isWin === false) { let hasPear = false this.shellProfiles = {} for (const file of ['.zshrc', '.zshenv', '.zshprofile', '.zlogin', '.profile', '.bashrc']) { const filepath = path.join(os.homedir(), file) let contents = null try { contents = fs.readFileSync(filepath, { encoding: 'utf-8' }) } catch {} if (contents !== null) { this.shellProfiles[file] = { filepath, hasPear: this.stmtrx.test(contents) } hasPear = hasPear || this.shellProfiles[file].hasPear } } if (hasPear) process.env.PATH = `${BIN}:${process.env.PATH}` } this.paths = process.env.PATH.split(path.delimiter) return this.paths.some((bin) => { return bin === BIN && fs.existsSync(path.join(BIN, isWin ? 'pear.cmd' : 'pear')) }) } #install () { const runtime = path.join('..', 'current', 'by-arch', process.platform + '-' + process.arch, 'bin', 'pear-runtime') fs.mkdirSync(BIN, { recursive: true }) if (isWin) { const ps1tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear') fs.writeFileSync(ps1tmp, `function pear { & "${runtime}" }; Export-ModuleMember -Function pear`) fs.renameSync(ps1tmp, path.join(BIN, 'pear.ps1')) const cmdtmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear') fs.writeFileSync(cmdtmp, `@echo off\n"${runtime}" %*`) fs.renameSync(cmdtmp, path.join(BIN, 'pear.cmd')) return new Promise((resolve, reject) => { spawn('cmd', ['/c', `setx PATH "${BIN};%PATH%"`]).on('exit', (code) => { const codes = [code] spawn('powershell', ['-Command', `$env:PATH = "${BIN};$env:PATH"`]).on('exit', (code) => { codes.push(code) if (codes[0] + codes[1] !== 0) { reject(new Error( 'Failed to set PATH in' + [ codes[0] ? ' cmd - exit code: ' + codes[0] : '', codes[1] ? ' powershell - exit code:' + codes[1] : '' ].filter(Boolean) )) return } process.env.PATH = `${BIN};${process.env.PATH}` resolve() }) }) }) } const comment = '# Added by Pear Runtime, configures system with Pear CLI\n' const profiles = Object.values(this.shellProfiles) if (profiles.length > 0) { for (const { filepath, hasPear } of profiles) { if (hasPear === false) fs.writeFileSync(filepath, '\n' + comment + this.statement + '\n', { flag: 'a' }) } } else { const bash = this.paths.some((bin) => fs.existsSync(path.join(bin, 'bash'))) const zsh = this.paths.some((bin) => fs.existsSync(path.join(bin, 'zsh'))) fs.writeFileSync(path.join(os.homedir(), bash ? '.bashrc' : '.profile'), this.statement + '\n', { flag: 'a' }) if (zsh) fs.writeFileSync(path.join(os.homedir(), '.zshrc'), '\n' + comment + this.statement + '\n', { flag: 'a' }) } const pear = path.join(BIN, 'pear') const tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear') fs.symlinkSync(runtime, tmp) fs.renameSync(tmp, pear) fs.chmodSync(pear, 0o755) process.env.PATH = `${BIN}:${process.env.PATH}` return Promise.resolve() } })