mirror of
https://github.com/atom/atom.git
synced 2026-01-14 17:38:03 -05:00
Merge branch 'master' into ns-modernize-build
This commit is contained in:
@@ -5,11 +5,12 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* C++ toolchain
|
||||
* C++11 toolchain
|
||||
* [Git](https://git-scm.com/)
|
||||
* [Node.js](https://nodejs.org/en/download/) (0.10.x or above)
|
||||
* [npm](https://www.npmjs.com/) v1.4.x or above (automatically bundled with Node.js)
|
||||
* Node.js (4.x or above) (Can be installed via [nvm](https://github.com/creationix/nvm)).
|
||||
* [npm](https://www.npmjs.com/) v3.10.5 or above (automatically bundled with Node.js)
|
||||
* `npm -v` to check the version.
|
||||
* `npm install -g npm` to upgrade if necessary.
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses python2.
|
||||
* You might need to run this command as `sudo`, depending on how you have set up [npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
* development headers for [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
|
||||
@@ -17,10 +18,18 @@ Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
### Ubuntu / Debian
|
||||
|
||||
* `sudo apt-get install build-essential git libgnome-keyring-dev fakeroot`
|
||||
* Instructions for [Node.js](https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager#debian-and-ubuntu-based-linux-distributions).
|
||||
* Make sure the command `node` is available after Node.js installation (some systems install it as `nodejs`).
|
||||
* Use `which node` to check if it is available.
|
||||
* Use `sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10` to update it.
|
||||
* Install Node.js and npm:
|
||||
* Install [nvm](https://github.com/creationix/nvm).
|
||||
* Run `nvm install 4` to install Node 4.x.
|
||||
* Run `npm install -g npm` to upgrade to the latest npm.
|
||||
* You may need to install a newer C++ compiler with C++11 support if script/bootstrap has errors:
|
||||
```sh
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-5 g++-5
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 80 --slave /usr/bin/g++ g++ /usr/bin/g++-5
|
||||
sudo update-alternatives --config gcc # choose gcc-5 from the list
|
||||
```
|
||||
|
||||
### Fedora / CentOS / RHEL
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"sinon": "1.17.4",
|
||||
"source-map-support": "^0.3.2",
|
||||
"temp": "0.8.1",
|
||||
"text-buffer": "9.2.3",
|
||||
"text-buffer": "9.2.6",
|
||||
"typescript-simple": "1.0.0",
|
||||
"underscore-plus": "^1.6.6",
|
||||
"winreg": "^1.2.1",
|
||||
@@ -87,13 +87,13 @@
|
||||
"autoflow": "0.27.0",
|
||||
"autosave": "0.23.1",
|
||||
"background-tips": "0.26.1",
|
||||
"bookmarks": "0.41.0",
|
||||
"bookmarks": "0.41.1",
|
||||
"bracket-matcher": "0.82.1",
|
||||
"command-palette": "0.38.0",
|
||||
"deprecation-cop": "0.54.1",
|
||||
"dev-live-reload": "0.47.0",
|
||||
"encoding-selector": "0.22.0",
|
||||
"exception-reporting": "0.38.1",
|
||||
"exception-reporting": "0.38.3",
|
||||
"find-and-replace": "0.201.0",
|
||||
"fuzzy-finder": "1.3.0",
|
||||
"git-diff": "1.1.0",
|
||||
|
||||
@@ -197,6 +197,21 @@ class AtomReporter
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time.textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
specTitle: (spec) ->
|
||||
parentDescs = []
|
||||
s = spec.suite
|
||||
while s
|
||||
parentDescs.unshift(s.description)
|
||||
s = s.parentSuite
|
||||
|
||||
suiteString = ""
|
||||
indent = ""
|
||||
for desc in parentDescs
|
||||
suiteString += indent + desc + "\n"
|
||||
indent += " "
|
||||
|
||||
"#{suiteString} #{indent} it #{spec.description}"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
@@ -204,6 +219,7 @@ class AtomReporter
|
||||
for spec in specs
|
||||
symbol = document.createElement('li')
|
||||
symbol.setAttribute('id', "spec-summary-#{spec.id}")
|
||||
symbol.setAttribute('title', @specTitle(spec))
|
||||
symbol.className = "spec-summary pending"
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
|
||||
@@ -1,159 +1,85 @@
|
||||
'use babel'
|
||||
/** @babel */
|
||||
/* eslint-env jasmine */
|
||||
|
||||
import child_process from 'child_process'
|
||||
import environmentHelpers from '../src/environment-helpers'
|
||||
import os from 'os'
|
||||
import updateProcessEnv from '../src/update-process-env'
|
||||
import dedent from 'dedent'
|
||||
|
||||
describe('Environment handling', () => {
|
||||
let originalEnv
|
||||
let options
|
||||
describe('updateProcessEnv(launchEnv)', function () {
|
||||
let originalProcessEnv, originalProcessPlatform
|
||||
|
||||
beforeEach(() => {
|
||||
originalEnv = process.env
|
||||
delete process._originalEnv
|
||||
options = {
|
||||
platform: process.platform,
|
||||
env: Object.assign({}, process.env)
|
||||
}
|
||||
beforeEach(function () {
|
||||
originalProcessEnv = process.env
|
||||
originalProcessPlatform = process.platform
|
||||
process.env = {}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv
|
||||
delete process._originalEnv
|
||||
afterEach(function () {
|
||||
process.env = originalProcessEnv
|
||||
process.platform = originalProcessPlatform
|
||||
})
|
||||
|
||||
describe('on macOS, when PWD is not set', () => {
|
||||
beforeEach(() => {
|
||||
options.platform = 'darwin'
|
||||
})
|
||||
describe('when the launch environment appears to come from a shell', function () {
|
||||
it('updates process.env to match the launch environment', function () {
|
||||
process.env = {
|
||||
WILL_BE_DELETED: 'hi',
|
||||
NODE_ENV: 'the-node-env',
|
||||
NODE_PATH: '/the/node/path',
|
||||
}
|
||||
const initialProcessEnv = process.env
|
||||
|
||||
describe('needsPatching', () => {
|
||||
it('returns true if PWD is unset', () => {
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = undefined
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = null
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
options.env.PWD = false
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
updateProcessEnv({PWD: '/the/dir', KEY1: 'value1', KEY2: 'value2'})
|
||||
expect(process.env).toEqual({
|
||||
PWD: '/the/dir',
|
||||
KEY1: 'value1',
|
||||
KEY2: 'value2',
|
||||
NODE_ENV: 'the-node-env',
|
||||
NODE_PATH: '/the/node/path',
|
||||
})
|
||||
|
||||
it('returns false if PWD is set', () => {
|
||||
options.env.PWD = 'xterm'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalize', () => {
|
||||
it('changes process.env if PWD is unset', () => {
|
||||
if (process.platform === 'win32') {
|
||||
return
|
||||
}
|
||||
delete options.env.PWD
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process._originalEnv).toBeDefined()
|
||||
expect(process._originalEnv).toBeTruthy()
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process.env).toBeTruthy()
|
||||
expect(process.env.PWD).toBeDefined()
|
||||
expect(process.env.PWD).toBeTruthy()
|
||||
expect(process.env.PATH).toBeDefined()
|
||||
expect(process.env.PATH).toBeTruthy()
|
||||
expect(process.env.ATOM_HOME).toBeDefined()
|
||||
expect(process.env.ATOM_HOME).toBeTruthy()
|
||||
})
|
||||
// See #11302. On Windows, `process.env` is a magic object that offers
|
||||
// case-insensitive environment variable matching, so we cannot replace it
|
||||
// with another object.
|
||||
expect(process.env).toBe(initialProcessEnv)
|
||||
})
|
||||
})
|
||||
|
||||
describe('on a platform other than macOS', () => {
|
||||
beforeEach(() => {
|
||||
options.platform = 'penguin'
|
||||
})
|
||||
describe('when the launch environment does not come from a shell', function () {
|
||||
describe('on osx', function () {
|
||||
it('updates process.env to match the environment in the user\'s login shell', function () {
|
||||
process.platform = 'darwin'
|
||||
process.env.SHELL = '/my/custom/bash'
|
||||
|
||||
describe('needsPatching', () => {
|
||||
it('returns false if PWD is set or unset', () => {
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = undefined
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = null
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = false
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
options.env.PWD = '/'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for linux', () => {
|
||||
options.platform = 'linux'
|
||||
options.PWD = '/'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for windows', () => {
|
||||
options.platform = 'win32'
|
||||
options.PWD = 'c:\\'
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalize', () => {
|
||||
it('does not change the environment', () => {
|
||||
if (process.platform === 'win32') {
|
||||
return
|
||||
}
|
||||
delete options.env.PWD
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process._originalEnv).toBeUndefined()
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process.env).toBeTruthy()
|
||||
expect(process.env.PATH).toBeDefined()
|
||||
expect(process.env.PATH).toBeTruthy()
|
||||
expect(process.env.PWD).toBeUndefined()
|
||||
expect(process.env.PATH).toBe(originalEnv.PATH)
|
||||
expect(process.env.ATOM_HOME).toBeDefined()
|
||||
expect(process.env.ATOM_HOME).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getFromShell', () => {
|
||||
describe('when things are configured properly', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
stdout: 'FOO=BAR' + os.EOL + 'TERM=xterm-something' + os.EOL +
|
||||
'PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path'
|
||||
stdout: dedent`
|
||||
FOO=BAR=BAZ=QUUX
|
||||
TERM=xterm-something
|
||||
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
|
||||
`
|
||||
})
|
||||
})
|
||||
|
||||
it('returns an object containing the information from the user\'s shell environment', () => {
|
||||
let env = environmentHelpers.getFromShell()
|
||||
expect(env.FOO).toEqual('BAR')
|
||||
expect(env.TERM).toEqual('xterm-something')
|
||||
expect(env.PATH).toEqual('/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path')
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync.mostRecentCall.args[0]).toBe('/my/custom/bash')
|
||||
expect(process.env).toEqual({
|
||||
FOO: 'BAR=BAZ=QUUX',
|
||||
TERM: 'xterm-something',
|
||||
PATH: '/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path'
|
||||
})
|
||||
|
||||
// Doesn't error
|
||||
updateProcessEnv(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when an error occurs launching the shell', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(child_process, 'spawnSync').andReturn({
|
||||
error: new Error('testing when an error occurs')
|
||||
})
|
||||
})
|
||||
describe('not on osx', function () {
|
||||
it('does not update process.env', function () {
|
||||
process.platform = 'win32'
|
||||
spyOn(child_process, 'spawnSync')
|
||||
process.env = {FOO: 'bar'}
|
||||
|
||||
it('returns undefined', () => {
|
||||
expect(environmentHelpers.getFromShell()).toBeUndefined()
|
||||
})
|
||||
|
||||
it('leaves the environment as-is when normalize() is called', () => {
|
||||
options.platform = 'darwin'
|
||||
delete options.env.PWD
|
||||
expect(environmentHelpers.needsPatching(options)).toBe(true)
|
||||
environmentHelpers.normalize(options)
|
||||
expect(process.env).toBeDefined()
|
||||
expect(process._originalEnv).toBeUndefined()
|
||||
updateProcessEnv(process.env)
|
||||
expect(child_process.spawnSync).not.toHaveBeenCalled()
|
||||
expect(process.env).toEqual({FOO: 'bar'})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5901,6 +5901,13 @@ describe "TextEditor", ->
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
it "does not throw errors after the marker's containing layer is destroyed", ->
|
||||
layer = editor.addMarkerLayer()
|
||||
marker = layer.markBufferRange([[2, 4], [6, 8]])
|
||||
decoration = editor.decorateMarker(marker, type: 'highlight', class: 'foo')
|
||||
layer.destroy()
|
||||
editor.decorationsStateForScreenRowRange(0, 5)
|
||||
|
||||
describe "::decorateMarkerLayer", ->
|
||||
it "based on the markers in the layer, includes multiple decoration objects with the same properties and different ranges in the object returned from ::decorationsStateForScreenRowRange", ->
|
||||
layer1 = editor.getBuffer().addMarkerLayer()
|
||||
|
||||
@@ -144,7 +144,7 @@ class DecorationManager extends Model
|
||||
else
|
||||
delete @overlayDecorationsById[decoration.id]
|
||||
|
||||
didDestroyDecoration: (decoration) ->
|
||||
didDestroyMarkerDecoration: (decoration) ->
|
||||
{marker} = decoration
|
||||
return unless decorations = @decorationsByMarkerId[marker.id]
|
||||
index = decorations.indexOf(decoration)
|
||||
|
||||
@@ -69,16 +69,16 @@ class Decoration
|
||||
@destroyed = false
|
||||
@markerDestroyDisposable = @marker.onDidDestroy => @destroy()
|
||||
|
||||
# Essential: Destroy this marker.
|
||||
# Essential: Destroy this marker decoration.
|
||||
#
|
||||
# If you own the marker, you should use {DisplayMarker::destroy} which will destroy
|
||||
# this decoration.
|
||||
# You can also destroy the marker if you own it, which will destroy this
|
||||
# decoration.
|
||||
destroy: ->
|
||||
return if @destroyed
|
||||
@markerDestroyDisposable.dispose()
|
||||
@markerDestroyDisposable = null
|
||||
@destroyed = true
|
||||
@decorationManager.didDestroyDecoration(this)
|
||||
@decorationManager.didDestroyMarkerDecoration(this)
|
||||
@emitter.emit 'did-destroy'
|
||||
@emitter.dispose()
|
||||
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
'use babel'
|
||||
|
||||
import {spawnSync} from 'child_process'
|
||||
import os from 'os'
|
||||
|
||||
// Gets a dump of the user's configured shell environment.
|
||||
//
|
||||
// Returns the output of the `env` command or `undefined` if there was an error.
|
||||
function getRawShellEnv () {
|
||||
let shell = getUserShell()
|
||||
|
||||
// The `-ilc` set of options was tested to work with the macOS v10.11
|
||||
// default-installed versions of bash, zsh, sh, and ksh. It *does not*
|
||||
// work with csh or tcsh.
|
||||
let results = spawnSync(shell, ['-ilc', 'env'], {encoding: 'utf8'})
|
||||
if (results.error || !results.stdout || results.stdout.length <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
return results.stdout
|
||||
}
|
||||
|
||||
function getUserShell () {
|
||||
if (process.env.SHELL) {
|
||||
return process.env.SHELL
|
||||
}
|
||||
|
||||
return '/bin/bash'
|
||||
}
|
||||
|
||||
// Gets the user's configured shell environment.
|
||||
//
|
||||
// Returns a copy of the user's shell enviroment.
|
||||
function getFromShell () {
|
||||
let shellEnvText = getRawShellEnv()
|
||||
if (!shellEnvText) {
|
||||
return
|
||||
}
|
||||
|
||||
let env = {}
|
||||
|
||||
for (let line of shellEnvText.split(os.EOL)) {
|
||||
if (line.includes('=')) {
|
||||
let components = line.split('=')
|
||||
if (components.length === 2) {
|
||||
env[components[0]] = components[1]
|
||||
} else {
|
||||
let k = components.shift()
|
||||
let v = components.join('=')
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
function needsPatching (options = { platform: process.platform, env: process.env }) {
|
||||
if (options.platform === 'darwin' && !options.env.PWD) {
|
||||
let shell = getUserShell()
|
||||
if (shell.endsWith('csh') || shell.endsWith('tcsh') || shell.endsWith('fish')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Fix for #11302 because `process.env` on Windows is a magic object that offers case-insensitive
|
||||
// environment variable matching. By always cloning to `process.env` we prevent breaking the
|
||||
// underlying functionality.
|
||||
function clone (to, from) {
|
||||
for (var key in to) {
|
||||
// Don't erase NODE_ENV. Fixes #12024
|
||||
if (key !== 'NODE_ENV') {
|
||||
delete to[key]
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(to, from)
|
||||
}
|
||||
|
||||
function normalize (options = {}) {
|
||||
if (options && options.env) {
|
||||
clone(process.env, options.env)
|
||||
}
|
||||
|
||||
if (!options.env) {
|
||||
options.env = process.env
|
||||
}
|
||||
|
||||
if (!options.platform) {
|
||||
options.platform = process.platform
|
||||
}
|
||||
|
||||
if (needsPatching(options)) {
|
||||
// Patch the `process.env` on startup to fix the problem first documented
|
||||
// in #4126. Retain the original in case someone needs it.
|
||||
let shellEnv = getFromShell()
|
||||
if (shellEnv && shellEnv.PATH) {
|
||||
process._originalEnv = Object.assign({}, process.env)
|
||||
clone(process.env, shellEnv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function replace (env) {
|
||||
if (!env || !env.PATH) {
|
||||
return
|
||||
}
|
||||
|
||||
clone(process.env, env)
|
||||
}
|
||||
|
||||
export default { getFromShell, needsPatching, normalize, replace }
|
||||
@@ -1,15 +1,13 @@
|
||||
# Like sands through the hourglass, so are the days of our lives.
|
||||
module.exports = ({blobStore}) ->
|
||||
environmentHelpers = require('./environment-helpers')
|
||||
updateProcessEnv = require('./update-process-env')
|
||||
path = require 'path'
|
||||
require './window'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
{ipcRenderer} = require 'electron'
|
||||
{resourcePath, isSpec, devMode, env} = getWindowLoadSettings()
|
||||
|
||||
# Set baseline environment
|
||||
environmentHelpers.normalize({env: env})
|
||||
env = process.env
|
||||
updateProcessEnv(env)
|
||||
|
||||
# Add application-specific exports to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
@@ -37,5 +35,5 @@ module.exports = ({blobStore}) ->
|
||||
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
|
||||
window.addEventListener('focus', windowFocused)
|
||||
ipcRenderer.on('environment', (event, env) ->
|
||||
environmentHelpers.replace(env)
|
||||
updateProcessEnv(env)
|
||||
)
|
||||
|
||||
@@ -113,9 +113,11 @@ function setupAtomHome ({setPortable}) {
|
||||
|
||||
try {
|
||||
atomHome = fs.realpathSync(atomHome)
|
||||
} finally {
|
||||
process.env.ATOM_HOME = atomHome
|
||||
} catch (e) {
|
||||
// Don't throw an error if atomHome doesn't exist.
|
||||
}
|
||||
|
||||
process.env.ATOM_HOME = atomHome
|
||||
}
|
||||
|
||||
function setupCompileCache () {
|
||||
|
||||
49
src/update-process-env.js
Normal file
49
src/update-process-env.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/** @babel */
|
||||
|
||||
import {spawnSync} from 'child_process'
|
||||
|
||||
const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set(['NODE_ENV', 'NODE_PATH'])
|
||||
|
||||
export default function updateProcessEnv (launchEnv) {
|
||||
let envToAssign
|
||||
if (launchEnv && launchEnv.PWD) {
|
||||
envToAssign = launchEnv
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
envToAssign = getEnvFromShell()
|
||||
}
|
||||
}
|
||||
|
||||
if (envToAssign) {
|
||||
for (let key in process.env) {
|
||||
if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
|
||||
delete process.env[key]
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in envToAssign) {
|
||||
if (!ENVIRONMENT_VARIABLES_TO_PRESERVE.has(key)) {
|
||||
process.env[key] = envToAssign[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEnvFromShell () {
|
||||
let shell = process.env.SHELL
|
||||
if (shell && (shell.endsWith('/bash') || shell.endsWith('/sh'))) {
|
||||
let {stdout} = spawnSync(shell, ['-ilc', 'env'], {encoding: 'utf8'})
|
||||
if (stdout) {
|
||||
let result = {}
|
||||
for (let line of stdout.split('\n')) {
|
||||
if (line.includes('=')) {
|
||||
let components = line.split('=')
|
||||
let key = components.shift()
|
||||
let value = components.join('=')
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user