mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-10-03 22:07:29 +03:00
Reformat all JS files using prettier
This commit is contained in:
parent
b17f4cbf68
commit
7f3f040628
@ -1,72 +1,75 @@
|
||||
const Chart = require('chart.js')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs-plus')
|
||||
const path = require('path')
|
||||
const Chart = require('chart.js');
|
||||
const glob = require('glob');
|
||||
const fs = require('fs-plus');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = async ({test, benchmarkPaths}) => {
|
||||
document.body.style.backgroundColor = '#ffffff'
|
||||
document.body.style.overflow = 'auto'
|
||||
module.exports = async ({ test, benchmarkPaths }) => {
|
||||
document.body.style.backgroundColor = '#ffffff';
|
||||
document.body.style.overflow = 'auto';
|
||||
|
||||
let paths = []
|
||||
let paths = [];
|
||||
for (const benchmarkPath of benchmarkPaths) {
|
||||
if (fs.isDirectorySync(benchmarkPath)) {
|
||||
paths = paths.concat(glob.sync(path.join(benchmarkPath, '**', '*.bench.js')))
|
||||
paths = paths.concat(
|
||||
glob.sync(path.join(benchmarkPath, '**', '*.bench.js'))
|
||||
);
|
||||
} else {
|
||||
paths.push(benchmarkPath)
|
||||
paths.push(benchmarkPath);
|
||||
}
|
||||
}
|
||||
|
||||
while (paths.length > 0) {
|
||||
const benchmark = require(paths.shift())({test})
|
||||
let results
|
||||
const benchmark = require(paths.shift())({ test });
|
||||
let results;
|
||||
if (benchmark instanceof Promise) {
|
||||
results = await benchmark
|
||||
results = await benchmark;
|
||||
} else {
|
||||
results = benchmark
|
||||
results = benchmark;
|
||||
}
|
||||
|
||||
const dataByBenchmarkName = {}
|
||||
for (const {name, duration, x} of results) {
|
||||
dataByBenchmarkName[name] = dataByBenchmarkName[name] || {points: []}
|
||||
dataByBenchmarkName[name].points.push({x, y: duration})
|
||||
const dataByBenchmarkName = {};
|
||||
for (const { name, duration, x } of results) {
|
||||
dataByBenchmarkName[name] = dataByBenchmarkName[name] || { points: [] };
|
||||
dataByBenchmarkName[name].points.push({ x, y: duration });
|
||||
}
|
||||
|
||||
const benchmarkContainer = document.createElement('div')
|
||||
document.body.appendChild(benchmarkContainer)
|
||||
const benchmarkContainer = document.createElement('div');
|
||||
document.body.appendChild(benchmarkContainer);
|
||||
for (const key in dataByBenchmarkName) {
|
||||
const data = dataByBenchmarkName[key]
|
||||
const data = dataByBenchmarkName[key];
|
||||
if (data.points.length > 1) {
|
||||
const canvas = document.createElement('canvas')
|
||||
benchmarkContainer.appendChild(canvas)
|
||||
const canvas = document.createElement('canvas');
|
||||
benchmarkContainer.appendChild(canvas);
|
||||
// eslint-disable-next-line no-new
|
||||
new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{label: key, fill: false, data: data.points}]
|
||||
datasets: [{ label: key, fill: false, data: data.points }]
|
||||
},
|
||||
options: {
|
||||
showLines: false,
|
||||
scales: {xAxes: [{type: 'linear', position: 'bottom'}]}
|
||||
scales: { xAxes: [{ type: 'linear', position: 'bottom' }] }
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const textualOutput = `${key}:\n\n` + data.points.map((p) => `${p.x}\t${p.y}`).join('\n')
|
||||
console.log(textualOutput)
|
||||
const textualOutput =
|
||||
`${key}:\n\n` + data.points.map(p => `${p.x}\t${p.y}`).join('\n');
|
||||
console.log(textualOutput);
|
||||
} else {
|
||||
const title = document.createElement('h2')
|
||||
title.textContent = key
|
||||
benchmarkContainer.appendChild(title)
|
||||
const duration = document.createElement('p')
|
||||
duration.textContent = `${data.points[0].y}ms`
|
||||
benchmarkContainer.appendChild(duration)
|
||||
const title = document.createElement('h2');
|
||||
title.textContent = key;
|
||||
benchmarkContainer.appendChild(title);
|
||||
const duration = document.createElement('p');
|
||||
duration.textContent = `${data.points[0].y}ms`;
|
||||
benchmarkContainer.appendChild(duration);
|
||||
|
||||
const textualOutput = `${key}: ${data.points[0].y}`
|
||||
console.log(textualOutput)
|
||||
const textualOutput = `${key}: ${data.points[0].y}`;
|
||||
console.log(textualOutput);
|
||||
}
|
||||
|
||||
await global.atom.reset()
|
||||
await global.atom.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
@ -1,88 +1,100 @@
|
||||
const {TextEditor, TextBuffer} = require('atom')
|
||||
const { TextEditor, TextBuffer } = require('atom');
|
||||
|
||||
const MIN_SIZE_IN_KB = 0 * 1024
|
||||
const MAX_SIZE_IN_KB = 10 * 1024
|
||||
const SIZE_STEP_IN_KB = 1024
|
||||
const LINE_TEXT = 'Lorem ipsum dolor sit amet\n'
|
||||
const TEXT = LINE_TEXT.repeat(Math.ceil(MAX_SIZE_IN_KB * 1024 / LINE_TEXT.length))
|
||||
const MIN_SIZE_IN_KB = 0 * 1024;
|
||||
const MAX_SIZE_IN_KB = 10 * 1024;
|
||||
const SIZE_STEP_IN_KB = 1024;
|
||||
const LINE_TEXT = 'Lorem ipsum dolor sit amet\n';
|
||||
const TEXT = LINE_TEXT.repeat(
|
||||
Math.ceil((MAX_SIZE_IN_KB * 1024) / LINE_TEXT.length)
|
||||
);
|
||||
|
||||
module.exports = async ({test}) => {
|
||||
const data = []
|
||||
module.exports = async ({ test }) => {
|
||||
const data = [];
|
||||
|
||||
document.body.appendChild(atom.workspace.getElement())
|
||||
document.body.appendChild(atom.workspace.getElement());
|
||||
|
||||
atom.packages.loadPackages()
|
||||
await atom.packages.activate()
|
||||
atom.packages.loadPackages();
|
||||
await atom.packages.activate();
|
||||
|
||||
for (let pane of atom.workspace.getPanes()) {
|
||||
pane.destroy()
|
||||
pane.destroy();
|
||||
}
|
||||
|
||||
for (let sizeInKB = MIN_SIZE_IN_KB; sizeInKB < MAX_SIZE_IN_KB; sizeInKB += SIZE_STEP_IN_KB) {
|
||||
const text = TEXT.slice(0, sizeInKB * 1024)
|
||||
console.log(text.length / 1024)
|
||||
for (
|
||||
let sizeInKB = MIN_SIZE_IN_KB;
|
||||
sizeInKB < MAX_SIZE_IN_KB;
|
||||
sizeInKB += SIZE_STEP_IN_KB
|
||||
) {
|
||||
const text = TEXT.slice(0, sizeInKB * 1024);
|
||||
console.log(text.length / 1024);
|
||||
|
||||
let t0 = window.performance.now()
|
||||
const buffer = new TextBuffer({text})
|
||||
const editor = new TextEditor({buffer, autoHeight: false, largeFileMode: true})
|
||||
atom.grammars.autoAssignLanguageMode(buffer)
|
||||
atom.workspace.getActivePane().activateItem(editor)
|
||||
let t1 = window.performance.now()
|
||||
let t0 = window.performance.now();
|
||||
const buffer = new TextBuffer({ text });
|
||||
const editor = new TextEditor({
|
||||
buffer,
|
||||
autoHeight: false,
|
||||
largeFileMode: true
|
||||
});
|
||||
atom.grammars.autoAssignLanguageMode(buffer);
|
||||
atom.workspace.getActivePane().activateItem(editor);
|
||||
let t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Opening a large file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
const tickDurations = []
|
||||
const tickDurations = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await timeout(50)
|
||||
t0 = window.performance.now()
|
||||
await timeout(0)
|
||||
t1 = window.performance.now()
|
||||
tickDurations[i] = t1 - t0
|
||||
await timeout(50);
|
||||
t0 = window.performance.now();
|
||||
await timeout(0);
|
||||
t1 = window.performance.now();
|
||||
tickDurations[i] = t1 - t0;
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: 'Max time event loop was blocked after opening a large file',
|
||||
x: sizeInKB,
|
||||
duration: Math.max(...tickDurations)
|
||||
})
|
||||
});
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.setCursorScreenPosition(editor.element.screenPositionForPixelPosition({
|
||||
top: 100,
|
||||
left: 30
|
||||
}))
|
||||
t1 = window.performance.now()
|
||||
t0 = window.performance.now();
|
||||
editor.setCursorScreenPosition(
|
||||
editor.element.screenPositionForPixelPosition({
|
||||
top: 100,
|
||||
left: 30
|
||||
})
|
||||
);
|
||||
t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Clicking the editor after opening a large file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.element.setScrollTop(editor.element.getScrollTop() + 100)
|
||||
t1 = window.performance.now()
|
||||
t0 = window.performance.now();
|
||||
editor.element.setScrollTop(editor.element.getScrollTop() + 100);
|
||||
t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Scrolling down after opening a large file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
editor.destroy()
|
||||
buffer.destroy()
|
||||
await timeout(10000)
|
||||
editor.destroy();
|
||||
buffer.destroy();
|
||||
await timeout(10000);
|
||||
}
|
||||
|
||||
atom.workspace.getElement().remove()
|
||||
atom.workspace.getElement().remove();
|
||||
|
||||
return data
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
function timeout (duration) {
|
||||
return new Promise((resolve) => setTimeout(resolve, duration))
|
||||
function timeout(duration) {
|
||||
return new Promise(resolve => setTimeout(resolve, duration));
|
||||
}
|
||||
|
@ -1,95 +1,105 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const {TextEditor, TextBuffer} = require('atom')
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { TextEditor, TextBuffer } = require('atom');
|
||||
|
||||
const SIZES_IN_KB = [
|
||||
512,
|
||||
1024,
|
||||
2048
|
||||
]
|
||||
const REPEATED_TEXT = fs.readFileSync(path.join(__dirname, '..', 'spec', 'fixtures', 'sample.js'), 'utf8').replace(/\n/g, '')
|
||||
const TEXT = REPEATED_TEXT.repeat(Math.ceil(SIZES_IN_KB[SIZES_IN_KB.length - 1] * 1024 / REPEATED_TEXT.length))
|
||||
const SIZES_IN_KB = [512, 1024, 2048];
|
||||
const REPEATED_TEXT = fs
|
||||
.readFileSync(
|
||||
path.join(__dirname, '..', 'spec', 'fixtures', 'sample.js'),
|
||||
'utf8'
|
||||
)
|
||||
.replace(/\n/g, '');
|
||||
const TEXT = REPEATED_TEXT.repeat(
|
||||
Math.ceil((SIZES_IN_KB[SIZES_IN_KB.length - 1] * 1024) / REPEATED_TEXT.length)
|
||||
);
|
||||
|
||||
module.exports = async ({test}) => {
|
||||
const data = []
|
||||
module.exports = async ({ test }) => {
|
||||
const data = [];
|
||||
|
||||
const workspaceElement = atom.workspace.getElement()
|
||||
document.body.appendChild(workspaceElement)
|
||||
const workspaceElement = atom.workspace.getElement();
|
||||
document.body.appendChild(workspaceElement);
|
||||
|
||||
atom.packages.loadPackages()
|
||||
await atom.packages.activate()
|
||||
atom.packages.loadPackages();
|
||||
await atom.packages.activate();
|
||||
|
||||
console.log(atom.getLoadSettings().resourcePath);
|
||||
|
||||
for (let pane of atom.workspace.getPanes()) {
|
||||
pane.destroy()
|
||||
pane.destroy();
|
||||
}
|
||||
|
||||
for (const sizeInKB of SIZES_IN_KB) {
|
||||
const text = TEXT.slice(0, sizeInKB * 1024)
|
||||
console.log(text.length / 1024)
|
||||
const text = TEXT.slice(0, sizeInKB * 1024);
|
||||
console.log(text.length / 1024);
|
||||
|
||||
let t0 = window.performance.now()
|
||||
const buffer = new TextBuffer({text})
|
||||
const editor = new TextEditor({buffer, autoHeight: false, largeFileMode: true})
|
||||
atom.grammars.assignLanguageMode(buffer, 'source.js')
|
||||
atom.workspace.getActivePane().activateItem(editor)
|
||||
let t1 = window.performance.now()
|
||||
let t0 = window.performance.now();
|
||||
const buffer = new TextBuffer({ text });
|
||||
const editor = new TextEditor({
|
||||
buffer,
|
||||
autoHeight: false,
|
||||
largeFileMode: true
|
||||
});
|
||||
atom.grammars.assignLanguageMode(buffer, 'source.js');
|
||||
atom.workspace.getActivePane().activateItem(editor);
|
||||
let t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
const tickDurations = []
|
||||
const tickDurations = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await timeout(50)
|
||||
t0 = window.performance.now()
|
||||
await timeout(0)
|
||||
t1 = window.performance.now()
|
||||
tickDurations[i] = t1 - t0
|
||||
await timeout(50);
|
||||
t0 = window.performance.now();
|
||||
await timeout(0);
|
||||
t1 = window.performance.now();
|
||||
tickDurations[i] = t1 - t0;
|
||||
}
|
||||
|
||||
data.push({
|
||||
name: 'Max time event loop was blocked after opening a large single-line file',
|
||||
name:
|
||||
'Max time event loop was blocked after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: Math.max(...tickDurations)
|
||||
})
|
||||
});
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.setCursorScreenPosition(editor.element.screenPositionForPixelPosition({
|
||||
top: 100,
|
||||
left: 30
|
||||
}))
|
||||
t1 = window.performance.now()
|
||||
t0 = window.performance.now();
|
||||
editor.setCursorScreenPosition(
|
||||
editor.element.screenPositionForPixelPosition({
|
||||
top: 100,
|
||||
left: 30
|
||||
})
|
||||
);
|
||||
t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Clicking the editor after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
t0 = window.performance.now()
|
||||
editor.element.setScrollTop(editor.element.getScrollTop() + 100)
|
||||
t1 = window.performance.now()
|
||||
t0 = window.performance.now();
|
||||
editor.element.setScrollTop(editor.element.getScrollTop() + 100);
|
||||
t1 = window.performance.now();
|
||||
|
||||
data.push({
|
||||
name: 'Scrolling down after opening a large single-line file',
|
||||
x: sizeInKB,
|
||||
duration: t1 - t0
|
||||
})
|
||||
});
|
||||
|
||||
editor.destroy()
|
||||
buffer.destroy()
|
||||
await timeout(10000)
|
||||
editor.destroy();
|
||||
buffer.destroy();
|
||||
await timeout(10000);
|
||||
}
|
||||
|
||||
workspaceElement.remove()
|
||||
workspaceElement.remove();
|
||||
|
||||
return data
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
function timeout (duration) {
|
||||
return new Promise((resolve) => setTimeout(resolve, duration))
|
||||
function timeout(duration) {
|
||||
return new Promise(resolve => setTimeout(resolve, duration));
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
const TextBuffer = require('text-buffer')
|
||||
const {Point, Range} = TextBuffer
|
||||
const {File, Directory} = require('pathwatcher')
|
||||
const {Emitter, Disposable, CompositeDisposable} = require('event-kit')
|
||||
const BufferedNodeProcess = require('../src/buffered-node-process')
|
||||
const BufferedProcess = require('../src/buffered-process')
|
||||
const GitRepository = require('../src/git-repository')
|
||||
const Notification = require('../src/notification')
|
||||
const {watchPath} = require('../src/path-watcher')
|
||||
const TextBuffer = require('text-buffer');
|
||||
const { Point, Range } = TextBuffer;
|
||||
const { File, Directory } = require('pathwatcher');
|
||||
const { Emitter, Disposable, CompositeDisposable } = require('event-kit');
|
||||
const BufferedNodeProcess = require('../src/buffered-node-process');
|
||||
const BufferedProcess = require('../src/buffered-process');
|
||||
const GitRepository = require('../src/git-repository');
|
||||
const Notification = require('../src/notification');
|
||||
const { watchPath } = require('../src/path-watcher');
|
||||
|
||||
const atomExport = {
|
||||
BufferedNodeProcess,
|
||||
@ -22,23 +22,23 @@ const atomExport = {
|
||||
Disposable,
|
||||
CompositeDisposable,
|
||||
watchPath
|
||||
}
|
||||
};
|
||||
|
||||
// Shell integration is required by both Squirrel and Settings-View
|
||||
if (process.platform === 'win32') {
|
||||
Object.defineProperty(atomExport, 'WinShell', {
|
||||
enumerable: true,
|
||||
get () {
|
||||
return require('../src/main-process/win-shell')
|
||||
get() {
|
||||
return require('../src/main-process/win-shell');
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// The following classes can't be used from a Task handler and should therefore
|
||||
// only be exported when not running as a child node process
|
||||
if (process.type === 'renderer') {
|
||||
atomExport.Task = require('../src/task')
|
||||
atomExport.TextEditor = require('../src/text-editor')
|
||||
atomExport.Task = require('../src/task');
|
||||
atomExport.TextEditor = require('../src/text-editor');
|
||||
}
|
||||
|
||||
module.exports = atomExport
|
||||
module.exports = atomExport;
|
||||
|
@ -1,7 +1,9 @@
|
||||
module.exports = require('electron').clipboard
|
||||
module.exports = require('electron').clipboard;
|
||||
|
||||
const Grim = require('grim')
|
||||
Grim.deprecate('Use `require("electron").clipboard` instead of `require("clipboard")`')
|
||||
const Grim = require('grim');
|
||||
Grim.deprecate(
|
||||
'Use `require("electron").clipboard` instead of `require("clipboard")`'
|
||||
);
|
||||
|
||||
// Ensure each package that requires this shim causes a deprecation warning
|
||||
delete require.cache[__filename]
|
||||
delete require.cache[__filename];
|
||||
|
@ -1,7 +1,9 @@
|
||||
module.exports = require('electron').ipcRenderer
|
||||
module.exports = require('electron').ipcRenderer;
|
||||
|
||||
const Grim = require('grim')
|
||||
Grim.deprecate('Use `require("electron").ipcRenderer` instead of `require("ipc")`')
|
||||
const Grim = require('grim');
|
||||
Grim.deprecate(
|
||||
'Use `require("electron").ipcRenderer` instead of `require("ipc")`'
|
||||
);
|
||||
|
||||
// Ensure each package that requires this shim causes a deprecation warning
|
||||
delete require.cache[__filename]
|
||||
delete require.cache[__filename];
|
||||
|
@ -1,7 +1,9 @@
|
||||
module.exports = require('electron').remote
|
||||
module.exports = require('electron').remote;
|
||||
|
||||
const Grim = require('grim')
|
||||
Grim.deprecate('Use `require("electron").remote` instead of `require("remote")`')
|
||||
const Grim = require('grim');
|
||||
Grim.deprecate(
|
||||
'Use `require("electron").remote` instead of `require("remote")`'
|
||||
);
|
||||
|
||||
// Ensure each package that requires this shim causes a deprecation warning
|
||||
delete require.cache[__filename]
|
||||
delete require.cache[__filename];
|
||||
|
@ -1,7 +1,7 @@
|
||||
module.exports = require('electron').shell
|
||||
module.exports = require('electron').shell;
|
||||
|
||||
const Grim = require('grim')
|
||||
Grim.deprecate('Use `require("electron").shell` instead of `require("shell")`')
|
||||
const Grim = require('grim');
|
||||
Grim.deprecate('Use `require("electron").shell` instead of `require("shell")`');
|
||||
|
||||
// Ensure each package that requires this shim causes a deprecation warning
|
||||
delete require.cache[__filename]
|
||||
delete require.cache[__filename];
|
||||
|
@ -1,7 +1,9 @@
|
||||
module.exports = require('electron').webFrame
|
||||
module.exports = require('electron').webFrame;
|
||||
|
||||
const Grim = require('grim')
|
||||
Grim.deprecate('Use `require("electron").webFrame` instead of `require("web-frame")`')
|
||||
const Grim = require('grim');
|
||||
Grim.deprecate(
|
||||
'Use `require("electron").webFrame` instead of `require("web-frame")`'
|
||||
);
|
||||
|
||||
// Ensure each package that requires this shim causes a deprecation warning
|
||||
delete require.cache[__filename]
|
||||
delete require.cache[__filename];
|
||||
|
@ -1,67 +1,67 @@
|
||||
const { CompositeDisposable, Emitter } = require('atom')
|
||||
const AboutView = require('./components/about-view')
|
||||
const { CompositeDisposable, Emitter } = require('atom');
|
||||
const AboutView = require('./components/about-view');
|
||||
|
||||
// Deferred requires
|
||||
let shell
|
||||
let shell;
|
||||
|
||||
module.exports = class About {
|
||||
constructor (initialState) {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
this.emitter = new Emitter()
|
||||
constructor(initialState) {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
this.emitter = new Emitter();
|
||||
|
||||
this.state = initialState
|
||||
this.state = initialState;
|
||||
this.views = {
|
||||
aboutView: null
|
||||
}
|
||||
};
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.workspace.addOpener(uriToOpen => {
|
||||
if (uriToOpen === this.state.uri) {
|
||||
return this.deserialize()
|
||||
return this.deserialize();
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.commands.add('atom-workspace', 'about:view-release-notes', () => {
|
||||
shell = shell || require('electron').shell
|
||||
shell = shell || require('electron').shell;
|
||||
shell.openExternal(
|
||||
this.state.updateManager.getReleaseNotesURLForCurrentVersion()
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
destroy () {
|
||||
if (this.views.aboutView) this.views.aboutView.destroy()
|
||||
this.views.aboutView = null
|
||||
destroy() {
|
||||
if (this.views.aboutView) this.views.aboutView.destroy();
|
||||
this.views.aboutView = null;
|
||||
|
||||
if (this.state.updateManager) this.state.updateManager.dispose()
|
||||
this.setState({ updateManager: null })
|
||||
if (this.state.updateManager) this.state.updateManager.dispose();
|
||||
this.setState({ updateManager: null });
|
||||
|
||||
this.subscriptions.dispose()
|
||||
this.subscriptions.dispose();
|
||||
}
|
||||
|
||||
setState (newState) {
|
||||
setState(newState) {
|
||||
if (newState && typeof newState === 'object') {
|
||||
let { state } = this
|
||||
this.state = Object.assign({}, state, newState)
|
||||
let { state } = this;
|
||||
this.state = Object.assign({}, state, newState);
|
||||
|
||||
this.didChange()
|
||||
this.didChange();
|
||||
}
|
||||
}
|
||||
|
||||
didChange () {
|
||||
this.emitter.emit('did-change')
|
||||
didChange() {
|
||||
this.emitter.emit('did-change');
|
||||
}
|
||||
|
||||
onDidChange (callback) {
|
||||
this.emitter.on('did-change', callback)
|
||||
onDidChange(callback) {
|
||||
this.emitter.on('did-change', callback);
|
||||
}
|
||||
|
||||
deserialize (state) {
|
||||
deserialize(state) {
|
||||
if (!this.views.aboutView) {
|
||||
this.setState(state)
|
||||
this.setState(state);
|
||||
|
||||
this.views.aboutView = new AboutView({
|
||||
uri: this.state.uri,
|
||||
@ -71,14 +71,14 @@ module.exports = class About {
|
||||
currentChromeVersion: this.state.currentChromeVersion,
|
||||
currentNodeVersion: this.state.currentNodeVersion,
|
||||
availableVersion: this.state.updateManager.getAvailableVersion()
|
||||
})
|
||||
this.handleStateChanges()
|
||||
});
|
||||
this.handleStateChanges();
|
||||
}
|
||||
|
||||
return this.views.aboutView
|
||||
return this.views.aboutView;
|
||||
}
|
||||
|
||||
handleStateChanges () {
|
||||
handleStateChanges() {
|
||||
this.onDidChange(() => {
|
||||
if (this.views.aboutView) {
|
||||
this.views.aboutView.update({
|
||||
@ -88,12 +88,12 @@ module.exports = class About {
|
||||
currentChromeVersion: this.state.currentChromeVersion,
|
||||
currentNodeVersion: this.state.currentNodeVersion,
|
||||
availableVersion: this.state.updateManager.getAvailableVersion()
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.state.updateManager.onDidChange(() => {
|
||||
this.didChange()
|
||||
})
|
||||
this.didChange();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,38 +1,38 @@
|
||||
const { CompositeDisposable } = require('atom')
|
||||
const etch = require('etch')
|
||||
const EtchComponent = require('../etch-component')
|
||||
const { CompositeDisposable } = require('atom');
|
||||
const etch = require('etch');
|
||||
const EtchComponent = require('../etch-component');
|
||||
|
||||
const $ = etch.dom
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class AboutStatusBar extends EtchComponent {
|
||||
constructor () {
|
||||
super()
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
constructor() {
|
||||
super();
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.tooltips.add(this.element, {
|
||||
title:
|
||||
'An update will be installed the next time Atom is relaunched.<br/><br/>Click the squirrel icon for more information.'
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
handleClick () {
|
||||
atom.workspace.open('atom://about')
|
||||
handleClick() {
|
||||
atom.workspace.open('atom://about');
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return $.div(
|
||||
{
|
||||
className: 'about-release-notes inline-block',
|
||||
onclick: this.handleClick.bind(this)
|
||||
},
|
||||
$.span({ type: 'button', className: 'icon icon-squirrel' })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
destroy () {
|
||||
super.destroy()
|
||||
this.subscriptions.dispose()
|
||||
destroy() {
|
||||
super.destroy();
|
||||
this.subscriptions.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,77 +1,77 @@
|
||||
const { Disposable } = require('atom')
|
||||
const etch = require('etch')
|
||||
const shell = require('shell')
|
||||
const AtomLogo = require('./atom-logo')
|
||||
const EtchComponent = require('../etch-component')
|
||||
const UpdateView = require('./update-view')
|
||||
const { Disposable } = require('atom');
|
||||
const etch = require('etch');
|
||||
const shell = require('shell');
|
||||
const AtomLogo = require('./atom-logo');
|
||||
const EtchComponent = require('../etch-component');
|
||||
const UpdateView = require('./update-view');
|
||||
|
||||
const $ = etch.dom
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class AboutView extends EtchComponent {
|
||||
handleAtomVersionClick (e) {
|
||||
e.preventDefault()
|
||||
atom.clipboard.write(this.props.currentAtomVersion)
|
||||
handleAtomVersionClick(e) {
|
||||
e.preventDefault();
|
||||
atom.clipboard.write(this.props.currentAtomVersion);
|
||||
}
|
||||
|
||||
handleElectronVersionClick (e) {
|
||||
e.preventDefault()
|
||||
atom.clipboard.write(this.props.currentElectronVersion)
|
||||
handleElectronVersionClick(e) {
|
||||
e.preventDefault();
|
||||
atom.clipboard.write(this.props.currentElectronVersion);
|
||||
}
|
||||
|
||||
handleChromeVersionClick (e) {
|
||||
e.preventDefault()
|
||||
atom.clipboard.write(this.props.currentChromeVersion)
|
||||
handleChromeVersionClick(e) {
|
||||
e.preventDefault();
|
||||
atom.clipboard.write(this.props.currentChromeVersion);
|
||||
}
|
||||
|
||||
handleNodeVersionClick (e) {
|
||||
e.preventDefault()
|
||||
atom.clipboard.write(this.props.currentNodeVersion)
|
||||
handleNodeVersionClick(e) {
|
||||
e.preventDefault();
|
||||
atom.clipboard.write(this.props.currentNodeVersion);
|
||||
}
|
||||
|
||||
handleReleaseNotesClick (e) {
|
||||
e.preventDefault()
|
||||
handleReleaseNotesClick(e) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(
|
||||
this.props.updateManager.getReleaseNotesURLForAvailableVersion()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
handleLicenseClick (e) {
|
||||
e.preventDefault()
|
||||
handleLicenseClick(e) {
|
||||
e.preventDefault();
|
||||
atom.commands.dispatch(
|
||||
atom.views.getView(atom.workspace),
|
||||
'application:open-license'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
handleTermsOfUseClick (e) {
|
||||
e.preventDefault()
|
||||
shell.openExternal('https://atom.io/terms')
|
||||
handleTermsOfUseClick(e) {
|
||||
e.preventDefault();
|
||||
shell.openExternal('https://atom.io/terms');
|
||||
}
|
||||
|
||||
handleHowToUpdateClick (e) {
|
||||
e.preventDefault()
|
||||
handleHowToUpdateClick(e) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(
|
||||
'https://flight-manual.atom.io/getting-started/sections/installing-atom/'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
handleShowMoreClick (e) {
|
||||
e.preventDefault()
|
||||
var showMoreDiv = document.querySelector('.show-more')
|
||||
var showMoreText = document.querySelector('.about-more-expand')
|
||||
handleShowMoreClick(e) {
|
||||
e.preventDefault();
|
||||
var showMoreDiv = document.querySelector('.show-more');
|
||||
var showMoreText = document.querySelector('.about-more-expand');
|
||||
switch (showMoreText.textContent) {
|
||||
case 'Show more':
|
||||
showMoreDiv.classList.toggle('hidden')
|
||||
showMoreText.textContent = 'Hide'
|
||||
break
|
||||
showMoreDiv.classList.toggle('hidden');
|
||||
showMoreText.textContent = 'Hide';
|
||||
break;
|
||||
case 'Hide':
|
||||
showMoreDiv.classList.toggle('hidden')
|
||||
showMoreText.textContent = 'Show more'
|
||||
break
|
||||
showMoreDiv.classList.toggle('hidden');
|
||||
showMoreText.textContent = 'Show more';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return $.div(
|
||||
{ className: 'pane-item native-key-bindings about' },
|
||||
$.div(
|
||||
@ -204,29 +204,29 @@ module.exports = class AboutView extends EtchComponent {
|
||||
'Atom Community'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
serialize () {
|
||||
serialize() {
|
||||
return {
|
||||
deserializer: this.constructor.name,
|
||||
uri: this.props.uri
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onDidChangeTitle () {
|
||||
return new Disposable()
|
||||
onDidChangeTitle() {
|
||||
return new Disposable();
|
||||
}
|
||||
|
||||
onDidChangeModified () {
|
||||
return new Disposable()
|
||||
onDidChangeModified() {
|
||||
return new Disposable();
|
||||
}
|
||||
|
||||
getTitle () {
|
||||
return 'About'
|
||||
getTitle() {
|
||||
return 'About';
|
||||
}
|
||||
|
||||
getIconName () {
|
||||
return 'info'
|
||||
getIconName() {
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
const etch = require('etch')
|
||||
const EtchComponent = require('../etch-component')
|
||||
const etch = require('etch');
|
||||
const EtchComponent = require('../etch-component');
|
||||
|
||||
const $ = etch.dom
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class AtomLogo extends EtchComponent {
|
||||
render () {
|
||||
render() {
|
||||
return $.svg(
|
||||
{
|
||||
className: 'about-logo',
|
||||
@ -74,6 +74,6 @@ module.exports = class AtomLogo extends EtchComponent {
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,46 +1,46 @@
|
||||
const etch = require('etch')
|
||||
const EtchComponent = require('../etch-component')
|
||||
const UpdateManager = require('../update-manager')
|
||||
const etch = require('etch');
|
||||
const EtchComponent = require('../etch-component');
|
||||
const UpdateManager = require('../update-manager');
|
||||
|
||||
const $ = etch.dom
|
||||
const $ = etch.dom;
|
||||
|
||||
module.exports = class UpdateView extends EtchComponent {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (
|
||||
this.props.updateManager.getAutoUpdatesEnabled() &&
|
||||
this.props.updateManager.getState() === UpdateManager.State.Idle
|
||||
) {
|
||||
this.props.updateManager.checkForUpdate()
|
||||
this.props.updateManager.checkForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
handleAutoUpdateCheckbox (e) {
|
||||
atom.config.set('core.automaticallyUpdate', e.target.checked)
|
||||
handleAutoUpdateCheckbox(e) {
|
||||
atom.config.set('core.automaticallyUpdate', e.target.checked);
|
||||
}
|
||||
|
||||
shouldUpdateActionButtonBeDisabled () {
|
||||
let { state } = this.props.updateManager
|
||||
shouldUpdateActionButtonBeDisabled() {
|
||||
let { state } = this.props.updateManager;
|
||||
return (
|
||||
state === UpdateManager.State.CheckingForUpdate ||
|
||||
state === UpdateManager.State.DownloadingUpdate
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
executeUpdateAction () {
|
||||
executeUpdateAction() {
|
||||
if (
|
||||
this.props.updateManager.state ===
|
||||
UpdateManager.State.UpdateAvailableToInstall
|
||||
) {
|
||||
this.props.updateManager.restartAndInstallUpdate()
|
||||
this.props.updateManager.restartAndInstallUpdate();
|
||||
} else {
|
||||
this.props.updateManager.checkForUpdate()
|
||||
this.props.updateManager.checkForUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
renderUpdateStatus () {
|
||||
let updateStatus = ''
|
||||
renderUpdateStatus() {
|
||||
let updateStatus = '';
|
||||
|
||||
switch (this.props.updateManager.state) {
|
||||
case UpdateManager.State.Idle:
|
||||
@ -52,8 +52,8 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
this.props.updateManager.getAutoUpdatesEnabled()
|
||||
? 'Atom will check for updates automatically'
|
||||
: 'Automatic updates are disabled please check manually'
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.CheckingForUpdate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-checking-for-updates' },
|
||||
@ -61,15 +61,15 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
{ className: 'about-updates-label icon icon-search' },
|
||||
'Checking for updates...'
|
||||
)
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.DownloadingUpdate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-downloading-update' },
|
||||
$.span({ className: 'loading loading-spinner-tiny inline-block' }),
|
||||
$.span({ className: 'about-updates-label' }, 'Downloading update')
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.UpdateAvailableToInstall:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-update-available-to-install' },
|
||||
@ -88,8 +88,8 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
},
|
||||
'Release Notes'
|
||||
)
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.UpToDate:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-up-to-date' },
|
||||
@ -98,8 +98,8 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
{ className: 'about-updates-label is-strong' },
|
||||
'Atom is up to date!'
|
||||
)
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.Unsupported:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-unsupported' },
|
||||
@ -114,8 +114,8 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
},
|
||||
'How to update'
|
||||
)
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
case UpdateManager.State.Error:
|
||||
updateStatus = $.div(
|
||||
{ className: 'about-updates-item app-update-error' },
|
||||
@ -124,14 +124,14 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
{ className: 'about-updates-label app-error-message is-strong' },
|
||||
this.props.updateManager.getErrorMessage()
|
||||
)
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return updateStatus
|
||||
return updateStatus;
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return $.div(
|
||||
{ className: 'about-updates group-start' },
|
||||
$.div(
|
||||
@ -176,6 +176,6 @@ module.exports = class UpdateView extends EtchComponent {
|
||||
$.span({}, 'Automatically download updates')
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,15 +1,15 @@
|
||||
const etch = require('etch')
|
||||
const etch = require('etch');
|
||||
|
||||
/*
|
||||
Public: Abstract class for handling the initialization
|
||||
boilerplate of an Etch component.
|
||||
*/
|
||||
module.exports = class EtchComponent {
|
||||
constructor (props) {
|
||||
this.props = props
|
||||
constructor(props) {
|
||||
this.props = props;
|
||||
|
||||
etch.initialize(this)
|
||||
EtchComponent.setScheduler(atom.views)
|
||||
etch.initialize(this);
|
||||
EtchComponent.setScheduler(atom.views);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -17,8 +17,8 @@ module.exports = class EtchComponent {
|
||||
|
||||
Returns a {Scheduler}
|
||||
*/
|
||||
static getScheduler () {
|
||||
return etch.getScheduler()
|
||||
static getScheduler() {
|
||||
return etch.getScheduler();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -26,8 +26,8 @@ module.exports = class EtchComponent {
|
||||
|
||||
* `scheduler` {Scheduler}
|
||||
*/
|
||||
static setScheduler (scheduler) {
|
||||
etch.setScheduler(scheduler)
|
||||
static setScheduler(scheduler) {
|
||||
etch.setScheduler(scheduler);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -37,20 +37,20 @@ module.exports = class EtchComponent {
|
||||
|
||||
* `props` an {Object} representing the properties you want to update
|
||||
*/
|
||||
update (props) {
|
||||
let oldProps = this.props
|
||||
this.props = Object.assign({}, oldProps, props)
|
||||
return etch.update(this)
|
||||
update(props) {
|
||||
let oldProps = this.props;
|
||||
this.props = Object.assign({}, oldProps, props);
|
||||
return etch.update(this);
|
||||
}
|
||||
|
||||
/*
|
||||
Public: Destroys the component, removing it from the DOM.
|
||||
*/
|
||||
destroy () {
|
||||
etch.destroy(this)
|
||||
destroy() {
|
||||
etch.destroy(this);
|
||||
}
|
||||
|
||||
render () {
|
||||
throw new Error('Etch components must implement a `render` method')
|
||||
render() {
|
||||
throw new Error('Etch components must implement a `render` method');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +1,26 @@
|
||||
const { CompositeDisposable } = require('atom')
|
||||
const semver = require('semver')
|
||||
const UpdateManager = require('./update-manager')
|
||||
const About = require('./about')
|
||||
const StatusBarView = require('./components/about-status-bar')
|
||||
let updateManager
|
||||
const { CompositeDisposable } = require('atom');
|
||||
const semver = require('semver');
|
||||
const UpdateManager = require('./update-manager');
|
||||
const About = require('./about');
|
||||
const StatusBarView = require('./components/about-status-bar');
|
||||
let updateManager;
|
||||
|
||||
// The local storage key for the available update version.
|
||||
const AvailableUpdateVersion = 'about:version-available'
|
||||
const AboutURI = 'atom://about'
|
||||
const AvailableUpdateVersion = 'about:version-available';
|
||||
const AboutURI = 'atom://about';
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
activate() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.createModel()
|
||||
this.createModel();
|
||||
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion)
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion);
|
||||
if (
|
||||
atom.getReleaseChannel() === 'dev' ||
|
||||
(availableVersion && semver.lte(availableVersion, atom.getVersion()))
|
||||
) {
|
||||
this.clearUpdateState()
|
||||
this.clearUpdateState();
|
||||
}
|
||||
|
||||
this.subscriptions.add(
|
||||
@ -32,48 +32,48 @@ module.exports = {
|
||||
window.localStorage.setItem(
|
||||
AvailableUpdateVersion,
|
||||
updateManager.getAvailableVersion()
|
||||
)
|
||||
this.showStatusBarIfNeeded()
|
||||
);
|
||||
this.showStatusBarIfNeeded();
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.commands.add('atom-workspace', 'about:clear-update-state', () => {
|
||||
this.clearUpdateState()
|
||||
this.clearUpdateState();
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
this.model.destroy()
|
||||
if (this.statusBarTile) this.statusBarTile.destroy()
|
||||
deactivate() {
|
||||
this.model.destroy();
|
||||
if (this.statusBarTile) this.statusBarTile.destroy();
|
||||
|
||||
if (updateManager) {
|
||||
updateManager.dispose()
|
||||
updateManager = undefined
|
||||
updateManager.dispose();
|
||||
updateManager = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
clearUpdateState () {
|
||||
window.localStorage.removeItem(AvailableUpdateVersion)
|
||||
clearUpdateState() {
|
||||
window.localStorage.removeItem(AvailableUpdateVersion);
|
||||
},
|
||||
|
||||
consumeStatusBar (statusBar) {
|
||||
this.statusBar = statusBar
|
||||
this.showStatusBarIfNeeded()
|
||||
consumeStatusBar(statusBar) {
|
||||
this.statusBar = statusBar;
|
||||
this.showStatusBarIfNeeded();
|
||||
},
|
||||
|
||||
deserializeAboutView (state) {
|
||||
deserializeAboutView(state) {
|
||||
if (!this.model) {
|
||||
this.createModel()
|
||||
this.createModel();
|
||||
}
|
||||
|
||||
return this.model.deserialize(state)
|
||||
return this.model.deserialize(state);
|
||||
},
|
||||
|
||||
createModel () {
|
||||
updateManager = updateManager || new UpdateManager()
|
||||
createModel() {
|
||||
updateManager = updateManager || new UpdateManager();
|
||||
|
||||
this.model = new About({
|
||||
uri: AboutURI,
|
||||
@ -82,28 +82,28 @@ module.exports = {
|
||||
currentChromeVersion: process.versions.chrome,
|
||||
currentNodeVersion: process.version,
|
||||
updateManager: updateManager
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
isUpdateAvailable () {
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion)
|
||||
return availableVersion && semver.gt(availableVersion, atom.getVersion())
|
||||
isUpdateAvailable() {
|
||||
let availableVersion = window.localStorage.getItem(AvailableUpdateVersion);
|
||||
return availableVersion && semver.gt(availableVersion, atom.getVersion());
|
||||
},
|
||||
|
||||
showStatusBarIfNeeded () {
|
||||
showStatusBarIfNeeded() {
|
||||
if (this.isUpdateAvailable() && this.statusBar) {
|
||||
let statusBarView = new StatusBarView()
|
||||
let statusBarView = new StatusBarView();
|
||||
|
||||
if (this.statusBarTile) {
|
||||
this.statusBarTile.destroy()
|
||||
this.statusBarTile.destroy();
|
||||
}
|
||||
|
||||
this.statusBarTile = this.statusBar.addRightTile({
|
||||
item: statusBarView,
|
||||
priority: -100
|
||||
})
|
||||
});
|
||||
|
||||
return this.statusBarTile
|
||||
return this.statusBarTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,46 +1,46 @@
|
||||
const { Emitter, CompositeDisposable } = require('atom')
|
||||
const { Emitter, CompositeDisposable } = require('atom');
|
||||
|
||||
const Unsupported = 'unsupported'
|
||||
const Idle = 'idle'
|
||||
const CheckingForUpdate = 'checking'
|
||||
const DownloadingUpdate = 'downloading'
|
||||
const UpdateAvailableToInstall = 'update-available'
|
||||
const UpToDate = 'no-update-available'
|
||||
const ErrorState = 'error'
|
||||
const Unsupported = 'unsupported';
|
||||
const Idle = 'idle';
|
||||
const CheckingForUpdate = 'checking';
|
||||
const DownloadingUpdate = 'downloading';
|
||||
const UpdateAvailableToInstall = 'update-available';
|
||||
const UpToDate = 'no-update-available';
|
||||
const ErrorState = 'error';
|
||||
|
||||
let UpdateManager = class UpdateManager {
|
||||
constructor () {
|
||||
this.emitter = new Emitter()
|
||||
this.currentVersion = atom.getVersion()
|
||||
this.availableVersion = atom.getVersion()
|
||||
this.resetState()
|
||||
this.listenForAtomEvents()
|
||||
constructor() {
|
||||
this.emitter = new Emitter();
|
||||
this.currentVersion = atom.getVersion();
|
||||
this.availableVersion = atom.getVersion();
|
||||
this.resetState();
|
||||
this.listenForAtomEvents();
|
||||
}
|
||||
|
||||
listenForAtomEvents () {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
listenForAtomEvents() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.autoUpdater.onDidBeginCheckingForUpdate(() => {
|
||||
this.setState(CheckingForUpdate)
|
||||
this.setState(CheckingForUpdate);
|
||||
}),
|
||||
atom.autoUpdater.onDidBeginDownloadingUpdate(() => {
|
||||
this.setState(DownloadingUpdate)
|
||||
this.setState(DownloadingUpdate);
|
||||
}),
|
||||
atom.autoUpdater.onDidCompleteDownloadingUpdate(({ releaseVersion }) => {
|
||||
this.setAvailableVersion(releaseVersion)
|
||||
this.setAvailableVersion(releaseVersion);
|
||||
}),
|
||||
atom.autoUpdater.onUpdateNotAvailable(() => {
|
||||
this.setState(UpToDate)
|
||||
this.setState(UpToDate);
|
||||
}),
|
||||
atom.autoUpdater.onUpdateError(() => {
|
||||
this.setState(ErrorState)
|
||||
this.setState(ErrorState);
|
||||
}),
|
||||
atom.config.observe('core.automaticallyUpdate', value => {
|
||||
this.autoUpdatesEnabled = value
|
||||
this.emitDidChange()
|
||||
this.autoUpdatesEnabled = value;
|
||||
this.emitDidChange();
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// TODO: When https://github.com/atom/electron/issues/4587 is closed we can add this support.
|
||||
// atom.autoUpdater.onUpdateAvailable =>
|
||||
@ -48,95 +48,95 @@ let UpdateManager = class UpdateManager {
|
||||
// @updateAvailable.addClass('is-shown')
|
||||
}
|
||||
|
||||
dispose () {
|
||||
this.subscriptions.dispose()
|
||||
dispose() {
|
||||
this.subscriptions.dispose();
|
||||
}
|
||||
|
||||
onDidChange (callback) {
|
||||
return this.emitter.on('did-change', callback)
|
||||
onDidChange(callback) {
|
||||
return this.emitter.on('did-change', callback);
|
||||
}
|
||||
|
||||
emitDidChange () {
|
||||
this.emitter.emit('did-change')
|
||||
emitDidChange() {
|
||||
this.emitter.emit('did-change');
|
||||
}
|
||||
|
||||
getAutoUpdatesEnabled () {
|
||||
getAutoUpdatesEnabled() {
|
||||
return (
|
||||
this.autoUpdatesEnabled && this.state !== UpdateManager.State.Unsupported
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setAutoUpdatesEnabled (enabled) {
|
||||
return atom.config.set('core.automaticallyUpdate', enabled)
|
||||
setAutoUpdatesEnabled(enabled) {
|
||||
return atom.config.set('core.automaticallyUpdate', enabled);
|
||||
}
|
||||
|
||||
getErrorMessage () {
|
||||
return atom.autoUpdater.getErrorMessage()
|
||||
getErrorMessage() {
|
||||
return atom.autoUpdater.getErrorMessage();
|
||||
}
|
||||
|
||||
getState () {
|
||||
return this.state
|
||||
getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
setState (state) {
|
||||
this.state = state
|
||||
this.emitDidChange()
|
||||
setState(state) {
|
||||
this.state = state;
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
resetState () {
|
||||
resetState() {
|
||||
this.state = atom.autoUpdater.platformSupportsUpdates()
|
||||
? atom.autoUpdater.getState()
|
||||
: Unsupported
|
||||
this.emitDidChange()
|
||||
: Unsupported;
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
getAvailableVersion () {
|
||||
return this.availableVersion
|
||||
getAvailableVersion() {
|
||||
return this.availableVersion;
|
||||
}
|
||||
|
||||
setAvailableVersion (version) {
|
||||
this.availableVersion = version
|
||||
setAvailableVersion(version) {
|
||||
this.availableVersion = version;
|
||||
|
||||
if (this.availableVersion !== this.currentVersion) {
|
||||
this.state = UpdateAvailableToInstall
|
||||
this.state = UpdateAvailableToInstall;
|
||||
} else {
|
||||
this.state = UpToDate
|
||||
this.state = UpToDate;
|
||||
}
|
||||
|
||||
this.emitDidChange()
|
||||
this.emitDidChange();
|
||||
}
|
||||
|
||||
checkForUpdate () {
|
||||
atom.autoUpdater.checkForUpdate()
|
||||
checkForUpdate() {
|
||||
atom.autoUpdater.checkForUpdate();
|
||||
}
|
||||
|
||||
restartAndInstallUpdate () {
|
||||
atom.autoUpdater.restartAndInstallUpdate()
|
||||
restartAndInstallUpdate() {
|
||||
atom.autoUpdater.restartAndInstallUpdate();
|
||||
}
|
||||
|
||||
getReleaseNotesURLForCurrentVersion () {
|
||||
return this.getReleaseNotesURLForVersion(this.currentVersion)
|
||||
getReleaseNotesURLForCurrentVersion() {
|
||||
return this.getReleaseNotesURLForVersion(this.currentVersion);
|
||||
}
|
||||
|
||||
getReleaseNotesURLForAvailableVersion () {
|
||||
return this.getReleaseNotesURLForVersion(this.availableVersion)
|
||||
getReleaseNotesURLForAvailableVersion() {
|
||||
return this.getReleaseNotesURLForVersion(this.availableVersion);
|
||||
}
|
||||
|
||||
getReleaseNotesURLForVersion (appVersion) {
|
||||
getReleaseNotesURLForVersion(appVersion) {
|
||||
// Dev versions will not have a releases page
|
||||
if (appVersion.indexOf('dev') > -1) {
|
||||
return 'https://atom.io/releases'
|
||||
return 'https://atom.io/releases';
|
||||
}
|
||||
|
||||
if (!appVersion.startsWith('v')) {
|
||||
appVersion = `v${appVersion}`
|
||||
appVersion = `v${appVersion}`;
|
||||
}
|
||||
|
||||
const releaseRepo =
|
||||
appVersion.indexOf('nightly') > -1 ? 'atom-nightly-releases' : 'atom'
|
||||
return `https://github.com/atom/${releaseRepo}/releases/tag/${appVersion}`
|
||||
appVersion.indexOf('nightly') > -1 ? 'atom-nightly-releases' : 'atom';
|
||||
return `https://github.com/atom/${releaseRepo}/releases/tag/${appVersion}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UpdateManager.State = {
|
||||
Unsupported: Unsupported,
|
||||
@ -146,6 +146,6 @@ UpdateManager.State = {
|
||||
UpdateAvailableToInstall: UpdateAvailableToInstall,
|
||||
UpToDate: UpToDate,
|
||||
Error: ErrorState
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = UpdateManager
|
||||
module.exports = UpdateManager;
|
||||
|
@ -1,28 +1,28 @@
|
||||
describe('About', () => {
|
||||
let workspaceElement
|
||||
let workspaceElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {}
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value
|
||||
})
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key]
|
||||
})
|
||||
return storage[key];
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
await atom.packages.activatePackage('about')
|
||||
})
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
it('deserializes correctly', () => {
|
||||
let deserializedAboutView = atom.deserializers.deserialize({
|
||||
deserializer: 'AboutView',
|
||||
uri: 'atom://about'
|
||||
})
|
||||
});
|
||||
|
||||
expect(deserializedAboutView).toBeTruthy()
|
||||
})
|
||||
expect(deserializedAboutView).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when the about:about-atom command is triggered', () => {
|
||||
it('shows the About Atom view', async () => {
|
||||
@ -30,70 +30,70 @@ describe('About', () => {
|
||||
// `toBeVisible()` matchers to work. Anything testing visibility or focus
|
||||
// requires that the workspaceElement is on the DOM. Tests that attach the
|
||||
// workspaceElement to the DOM are generally slower than those off DOM.
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
expect(workspaceElement.querySelector('.about')).not.toExist()
|
||||
await atom.workspace.open('atom://about')
|
||||
expect(workspaceElement.querySelector('.about')).not.toExist();
|
||||
await atom.workspace.open('atom://about');
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
expect(aboutElement).toBeVisible()
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
expect(aboutElement).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the Atom version number is clicked', () => {
|
||||
it('copies the version number to the clipboard', async () => {
|
||||
await atom.workspace.open('atom://about')
|
||||
await atom.workspace.open('atom://about');
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
let versionContainer = aboutElement.querySelector('.atom')
|
||||
versionContainer.click()
|
||||
expect(atom.clipboard.read()).toBe(atom.getVersion())
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let versionContainer = aboutElement.querySelector('.atom');
|
||||
versionContainer.click();
|
||||
expect(atom.clipboard.read()).toBe(atom.getVersion());
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the show more link is clicked', () => {
|
||||
it('expands to show additional version numbers', async () => {
|
||||
await atom.workspace.open('atom://about')
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
await atom.workspace.open('atom://about');
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
let showMoreElement = aboutElement.querySelector('.show-more-expand')
|
||||
let moreInfoElement = workspaceElement.querySelector('.show-more')
|
||||
showMoreElement.click()
|
||||
expect(moreInfoElement).toBeVisible()
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let showMoreElement = aboutElement.querySelector('.show-more-expand');
|
||||
let moreInfoElement = workspaceElement.querySelector('.show-more');
|
||||
showMoreElement.click();
|
||||
expect(moreInfoElement).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the Electron version number is clicked', () => {
|
||||
it('copies the version number to the clipboard', async () => {
|
||||
await atom.workspace.open('atom://about')
|
||||
await atom.workspace.open('atom://about');
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
let versionContainer = aboutElement.querySelector('.electron')
|
||||
versionContainer.click()
|
||||
expect(atom.clipboard.read()).toBe(process.versions.electron)
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let versionContainer = aboutElement.querySelector('.electron');
|
||||
versionContainer.click();
|
||||
expect(atom.clipboard.read()).toBe(process.versions.electron);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the Chrome version number is clicked', () => {
|
||||
it('copies the version number to the clipboard', async () => {
|
||||
await atom.workspace.open('atom://about')
|
||||
await atom.workspace.open('atom://about');
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
let versionContainer = aboutElement.querySelector('.chrome')
|
||||
versionContainer.click()
|
||||
expect(atom.clipboard.read()).toBe(process.versions.chrome)
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let versionContainer = aboutElement.querySelector('.chrome');
|
||||
versionContainer.click();
|
||||
expect(atom.clipboard.read()).toBe(process.versions.chrome);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the Node version number is clicked', () => {
|
||||
it('copies the version number to the clipboard', async () => {
|
||||
await atom.workspace.open('atom://about')
|
||||
await atom.workspace.open('atom://about');
|
||||
|
||||
let aboutElement = workspaceElement.querySelector('.about')
|
||||
let versionContainer = aboutElement.querySelector('.node')
|
||||
versionContainer.click()
|
||||
expect(atom.clipboard.read()).toBe(process.version)
|
||||
})
|
||||
})
|
||||
})
|
||||
let aboutElement = workspaceElement.querySelector('.about');
|
||||
let versionContainer = aboutElement.querySelector('.node');
|
||||
versionContainer.click();
|
||||
expect(atom.clipboard.read()).toBe(process.version);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,179 +1,183 @@
|
||||
const { conditionPromise } = require('./helpers/async-spec-helpers')
|
||||
const MockUpdater = require('./mocks/updater')
|
||||
const { conditionPromise } = require('./helpers/async-spec-helpers');
|
||||
const MockUpdater = require('./mocks/updater');
|
||||
|
||||
describe('the status bar', () => {
|
||||
let atomVersion
|
||||
let workspaceElement
|
||||
let atomVersion;
|
||||
let workspaceElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {}
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value
|
||||
})
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key]
|
||||
})
|
||||
return storage[key];
|
||||
});
|
||||
spyOn(atom, 'getVersion').andCallFake(() => {
|
||||
return atomVersion
|
||||
})
|
||||
return atomVersion;
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
|
||||
await atom.packages.activatePackage('status-bar')
|
||||
await atom.workspace.open('sample.js')
|
||||
})
|
||||
await atom.packages.activatePackage('status-bar');
|
||||
await atom.workspace.open('sample.js');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await atom.packages.deactivatePackage('about')
|
||||
await atom.packages.deactivatePackage('status-bar')
|
||||
})
|
||||
await atom.packages.deactivatePackage('about');
|
||||
await atom.packages.deactivatePackage('status-bar');
|
||||
});
|
||||
|
||||
describe('on a stable version', function () {
|
||||
describe('on a stable version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3'
|
||||
atomVersion = '1.2.3';
|
||||
|
||||
await atom.packages.activatePackage('about')
|
||||
})
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an update', () => {
|
||||
it('shows the view when the update finishes downloading', () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
})
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
describe('clicking on the status', () => {
|
||||
it('opens the about page', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
workspaceElement.querySelector('.about-release-notes').click()
|
||||
await conditionPromise(() => workspaceElement.querySelector('.about'))
|
||||
expect(workspaceElement.querySelector('.about')).toExist()
|
||||
})
|
||||
})
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
workspaceElement.querySelector('.about-release-notes').click();
|
||||
await conditionPromise(() =>
|
||||
workspaceElement.querySelector('.about')
|
||||
);
|
||||
expect(workspaceElement.querySelector('.about')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
it('continues to show the squirrel until Atom is updated to the new version', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.activatePackage('about')
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
await atom.packages.activatePackage('about');
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
atomVersion = '42.0.0'
|
||||
await atom.packages.activatePackage('about')
|
||||
atomVersion = '42.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
it('does not show the view if Atom is updated to a newer version than notified', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
|
||||
atomVersion = '43.0.0'
|
||||
await atom.packages.activatePackage('about')
|
||||
atomVersion = '43.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
})
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on a beta version', function () {
|
||||
describe('on a beta version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3-beta4'
|
||||
atomVersion = '1.2.3-beta4';
|
||||
|
||||
await atom.packages.activatePackage('about')
|
||||
})
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an update', () => {
|
||||
it('shows the view when the update finishes downloading', () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
})
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
describe('clicking on the status', () => {
|
||||
it('opens the about page', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
workspaceElement.querySelector('.about-release-notes').click()
|
||||
await conditionPromise(() => workspaceElement.querySelector('.about'))
|
||||
expect(workspaceElement.querySelector('.about')).toExist()
|
||||
})
|
||||
})
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
workspaceElement.querySelector('.about-release-notes').click();
|
||||
await conditionPromise(() =>
|
||||
workspaceElement.querySelector('.about')
|
||||
);
|
||||
expect(workspaceElement.querySelector('.about')).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
it('continues to show the squirrel until Atom is updated to the new version', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.activatePackage('about')
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes')
|
||||
await atom.packages.activatePackage('about');
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).toContain('.about-release-notes');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
|
||||
atomVersion = '42.0.0'
|
||||
await atom.packages.activatePackage('about')
|
||||
atomVersion = '42.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
|
||||
it('does not show the view if Atom is updated to a newer version than notified', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
|
||||
await atom.packages.deactivatePackage('about')
|
||||
await atom.packages.deactivatePackage('about');
|
||||
|
||||
atomVersion = '43.0.0'
|
||||
await atom.packages.activatePackage('about')
|
||||
atomVersion = '43.0.0';
|
||||
await atom.packages.activatePackage('about');
|
||||
|
||||
await Promise.resolve() // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
})
|
||||
await Promise.resolve(); // Service consumption hooks are deferred until the next tick
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on a development version', function () {
|
||||
describe('on a development version', function() {
|
||||
beforeEach(async () => {
|
||||
atomVersion = '1.2.3-dev-0123abcd'
|
||||
atomVersion = '1.2.3-dev-0123abcd';
|
||||
|
||||
await atom.packages.activatePackage('about')
|
||||
})
|
||||
await atom.packages.activatePackage('about');
|
||||
});
|
||||
|
||||
describe('with no update', () => {
|
||||
it('does not show the view', () => {
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a previously downloaded update', () => {
|
||||
it('does not show the view', () => {
|
||||
window.localStorage.setItem('about:version-available', '42.0.0')
|
||||
window.localStorage.setItem('about:version-available', '42.0.0');
|
||||
|
||||
expect(workspaceElement).not.toContain('.about-release-notes')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(workspaceElement).not.toContain('.about-release-notes');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,26 +1,26 @@
|
||||
/** @babel */
|
||||
|
||||
const { now } = Date
|
||||
const { setTimeout } = global
|
||||
const { now } = Date;
|
||||
const { setTimeout } = global;
|
||||
|
||||
export async function conditionPromise (condition) {
|
||||
const startTime = now()
|
||||
export async function conditionPromise(condition) {
|
||||
const startTime = now();
|
||||
|
||||
while (true) {
|
||||
await timeoutPromise(100)
|
||||
await timeoutPromise(100);
|
||||
|
||||
if (await condition()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (now() - startTime > 5000) {
|
||||
throw new Error('Timed out waiting on condition')
|
||||
throw new Error('Timed out waiting on condition');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function timeoutPromise (timeout) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, timeout)
|
||||
})
|
||||
export function timeoutPromise(timeout) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, timeout);
|
||||
});
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
module.exports = {
|
||||
updateError () {
|
||||
atom.autoUpdater.emitter.emit('update-error')
|
||||
updateError() {
|
||||
atom.autoUpdater.emitter.emit('update-error');
|
||||
},
|
||||
|
||||
checkForUpdate () {
|
||||
atom.autoUpdater.emitter.emit('did-begin-checking-for-update')
|
||||
checkForUpdate() {
|
||||
atom.autoUpdater.emitter.emit('did-begin-checking-for-update');
|
||||
},
|
||||
|
||||
updateNotAvailable () {
|
||||
atom.autoUpdater.emitter.emit('update-not-available')
|
||||
updateNotAvailable() {
|
||||
atom.autoUpdater.emitter.emit('update-not-available');
|
||||
},
|
||||
|
||||
downloadUpdate () {
|
||||
atom.autoUpdater.emitter.emit('did-begin-downloading-update')
|
||||
downloadUpdate() {
|
||||
atom.autoUpdater.emitter.emit('did-begin-downloading-update');
|
||||
},
|
||||
|
||||
finishDownloadingUpdate (releaseVersion) {
|
||||
finishDownloadingUpdate(releaseVersion) {
|
||||
atom.autoUpdater.emitter.emit('did-complete-downloading-update', {
|
||||
releaseVersion
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,32 +1,32 @@
|
||||
const UpdateManager = require('../lib/update-manager')
|
||||
const UpdateManager = require('../lib/update-manager');
|
||||
|
||||
describe('UpdateManager', () => {
|
||||
let updateManager
|
||||
let updateManager;
|
||||
|
||||
beforeEach(() => {
|
||||
updateManager = new UpdateManager()
|
||||
})
|
||||
updateManager = new UpdateManager();
|
||||
});
|
||||
|
||||
describe('::getReleaseNotesURLForVersion', () => {
|
||||
it('returns atom.io releases when dev version', () => {
|
||||
expect(
|
||||
updateManager.getReleaseNotesURLForVersion('1.7.0-dev-e44b57d')
|
||||
).toContain('atom.io/releases')
|
||||
})
|
||||
).toContain('atom.io/releases');
|
||||
});
|
||||
|
||||
it('returns the page for the release when not a dev version', () => {
|
||||
expect(updateManager.getReleaseNotesURLForVersion('1.7.0')).toContain(
|
||||
'atom/atom/releases/tag/v1.7.0'
|
||||
)
|
||||
);
|
||||
expect(updateManager.getReleaseNotesURLForVersion('v1.7.0')).toContain(
|
||||
'atom/atom/releases/tag/v1.7.0'
|
||||
)
|
||||
);
|
||||
expect(
|
||||
updateManager.getReleaseNotesURLForVersion('1.7.0-beta10')
|
||||
).toContain('atom/atom/releases/tag/v1.7.0-beta10')
|
||||
).toContain('atom/atom/releases/tag/v1.7.0-beta10');
|
||||
expect(
|
||||
updateManager.getReleaseNotesURLForVersion('1.7.0-nightly10')
|
||||
).toContain('atom/atom-nightly-releases/releases/tag/v1.7.0-nightly10')
|
||||
})
|
||||
})
|
||||
})
|
||||
).toContain('atom/atom-nightly-releases/releases/tag/v1.7.0-nightly10');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,385 +1,387 @@
|
||||
const { shell } = require('electron')
|
||||
const main = require('../lib/main')
|
||||
const AboutView = require('../lib/components/about-view')
|
||||
const UpdateView = require('../lib/components/update-view')
|
||||
const MockUpdater = require('./mocks/updater')
|
||||
const { shell } = require('electron');
|
||||
const main = require('../lib/main');
|
||||
const AboutView = require('../lib/components/about-view');
|
||||
const UpdateView = require('../lib/components/update-view');
|
||||
const MockUpdater = require('./mocks/updater');
|
||||
|
||||
describe('UpdateView', () => {
|
||||
let aboutElement
|
||||
let updateManager
|
||||
let workspaceElement
|
||||
let scheduler
|
||||
let aboutElement;
|
||||
let updateManager;
|
||||
let workspaceElement;
|
||||
let scheduler;
|
||||
|
||||
beforeEach(async () => {
|
||||
let storage = {}
|
||||
let storage = {};
|
||||
|
||||
spyOn(window.localStorage, 'setItem').andCallFake((key, value) => {
|
||||
storage[key] = value
|
||||
})
|
||||
storage[key] = value;
|
||||
});
|
||||
spyOn(window.localStorage, 'getItem').andCallFake(key => {
|
||||
return storage[key]
|
||||
})
|
||||
return storage[key];
|
||||
});
|
||||
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
await atom.packages.activatePackage('about')
|
||||
spyOn(atom.autoUpdater, 'getState').andReturn('idle')
|
||||
spyOn(atom.autoUpdater, 'checkForUpdate')
|
||||
spyOn(atom.autoUpdater, 'platformSupportsUpdates').andReturn(true)
|
||||
})
|
||||
workspaceElement = atom.views.getView(atom.workspace);
|
||||
await atom.packages.activatePackage('about');
|
||||
spyOn(atom.autoUpdater, 'getState').andReturn('idle');
|
||||
spyOn(atom.autoUpdater, 'checkForUpdate');
|
||||
spyOn(atom.autoUpdater, 'platformSupportsUpdates').andReturn(true);
|
||||
});
|
||||
|
||||
describe('when the About page is open', () => {
|
||||
beforeEach(async () => {
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
await atom.workspace.open('atom://about')
|
||||
aboutElement = workspaceElement.querySelector('.about')
|
||||
updateManager = main.model.state.updateManager
|
||||
scheduler = AboutView.getScheduler()
|
||||
})
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
await atom.workspace.open('atom://about');
|
||||
aboutElement = workspaceElement.querySelector('.about');
|
||||
updateManager = main.model.state.updateManager;
|
||||
scheduler = AboutView.getScheduler();
|
||||
});
|
||||
|
||||
describe('when the updates are not supported by the platform', () => {
|
||||
beforeEach(async () => {
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(false)
|
||||
updateManager.resetState()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
})
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(false);
|
||||
updateManager.resetState();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
});
|
||||
|
||||
it('hides the auto update UI and shows the update instructions link', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button')
|
||||
).not.toBeVisible()
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates')
|
||||
).not.toBeVisible()
|
||||
})
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('opens the update instructions page when the instructions link is clicked', async () => {
|
||||
spyOn(shell, 'openExternal')
|
||||
spyOn(shell, 'openExternal');
|
||||
let link = aboutElement.querySelector(
|
||||
'.app-unsupported .about-updates-instructions'
|
||||
)
|
||||
link.click()
|
||||
);
|
||||
link.click();
|
||||
|
||||
let args = shell.openExternal.mostRecentCall.args
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(args[0]).toContain('installing-atom')
|
||||
})
|
||||
})
|
||||
let args = shell.openExternal.mostRecentCall.args;
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(args[0]).toContain('installing-atom');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when updates are supported by the platform', () => {
|
||||
beforeEach(async () => {
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(true)
|
||||
updateManager.resetState()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
})
|
||||
atom.autoUpdater.platformSupportsUpdates.andReturn(true);
|
||||
updateManager.resetState();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
});
|
||||
|
||||
it('shows the auto update UI', () => {
|
||||
expect(aboutElement.querySelector('.about-updates')).toBeVisible()
|
||||
})
|
||||
expect(aboutElement.querySelector('.about-updates')).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the correct panels when the app checks for updates and there is no update available', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.checkForUpdate()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible()
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.updateNotAvailable()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible()
|
||||
MockUpdater.updateNotAvailable();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible()
|
||||
})
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the correct panels when the app checks for updates and encounters an error', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
|
||||
MockUpdater.checkForUpdate()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible()
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
|
||||
spyOn(atom.autoUpdater, 'getErrorMessage').andReturn('an error message')
|
||||
MockUpdater.updateError()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
expect(aboutElement.querySelector('.app-update-error')).toBeVisible()
|
||||
spyOn(atom.autoUpdater, 'getErrorMessage').andReturn(
|
||||
'an error message'
|
||||
);
|
||||
MockUpdater.updateError();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(aboutElement.querySelector('.app-update-error')).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-error-message').textContent
|
||||
).toBe('an error message')
|
||||
).toBe('an error message');
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible()
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now')
|
||||
})
|
||||
).toBe('Check now');
|
||||
});
|
||||
|
||||
it('shows the correct panels and button states when the app checks for updates and an update is downloaded', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now')
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.checkForUpdate()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
MockUpdater.checkForUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible()
|
||||
expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now')
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.downloadUpdate()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
MockUpdater.downloadUpdate();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible()
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
// TODO: at some point it would be nice to be able to cancel an update download, and then this would be a cancel button
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now')
|
||||
).toBe('Check now');
|
||||
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
await scheduler.getNextUpdatePromise()
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).not.toBeVisible()
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-update-available-to-install')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
|
||||
expect(
|
||||
aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-version'
|
||||
).textContent
|
||||
).toBe('42.0.0')
|
||||
).toBe('42.0.0');
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Restart and install')
|
||||
})
|
||||
).toBe('Restart and install');
|
||||
});
|
||||
|
||||
it('opens the release notes for the downloaded release when the release notes link are clicked', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('1.2.3')
|
||||
await scheduler.getNextUpdatePromise()
|
||||
MockUpdater.finishDownloadingUpdate('1.2.3');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
spyOn(shell, 'openExternal')
|
||||
spyOn(shell, 'openExternal');
|
||||
let link = aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-release-notes'
|
||||
)
|
||||
link.click()
|
||||
);
|
||||
link.click();
|
||||
|
||||
let args = shell.openExternal.mostRecentCall.args
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(args[0]).toContain('/v1.2.3')
|
||||
})
|
||||
let args = shell.openExternal.mostRecentCall.args;
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(args[0]).toContain('/v1.2.3');
|
||||
});
|
||||
|
||||
it('executes checkForUpdate() when the check for update button is clicked', () => {
|
||||
let button = aboutElement.querySelector('.about-update-action-button')
|
||||
button.click()
|
||||
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled()
|
||||
})
|
||||
let button = aboutElement.querySelector('.about-update-action-button');
|
||||
button.click();
|
||||
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('executes restartAndInstallUpdate() when the restart and install button is clicked', async () => {
|
||||
spyOn(atom.autoUpdater, 'restartAndInstallUpdate')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
await scheduler.getNextUpdatePromise()
|
||||
spyOn(atom.autoUpdater, 'restartAndInstallUpdate');
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
let button = aboutElement.querySelector('.about-update-action-button')
|
||||
button.click()
|
||||
expect(atom.autoUpdater.restartAndInstallUpdate).toHaveBeenCalled()
|
||||
})
|
||||
let button = aboutElement.querySelector('.about-update-action-button');
|
||||
button.click();
|
||||
expect(atom.autoUpdater.restartAndInstallUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("starts in the same state as atom's AutoUpdateManager", async () => {
|
||||
atom.autoUpdater.getState.andReturn('downloading')
|
||||
updateManager.resetState()
|
||||
atom.autoUpdater.getState.andReturn('downloading');
|
||||
updateManager.resetState();
|
||||
|
||||
await scheduler.getNextUpdatePromise()
|
||||
await scheduler.getNextUpdatePromise();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-checking-for-updates')
|
||||
).not.toBeVisible()
|
||||
).not.toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.app-downloading-update')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Check now')
|
||||
})
|
||||
).toBe('Check now');
|
||||
});
|
||||
|
||||
describe('when core.automaticallyUpdate is toggled', () => {
|
||||
beforeEach(async () => {
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(true)
|
||||
atom.autoUpdater.checkForUpdate.reset()
|
||||
})
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
|
||||
atom.autoUpdater.checkForUpdate.reset();
|
||||
});
|
||||
|
||||
it('shows the auto update UI', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates input').checked
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
.textContent
|
||||
).toBe('Atom will check for updates automatically')
|
||||
).toBe('Atom will check for updates automatically');
|
||||
|
||||
atom.config.set('core.automaticallyUpdate', false)
|
||||
await scheduler.getNextUpdatePromise()
|
||||
atom.config.set('core.automaticallyUpdate', false);
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates input').checked
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
.textContent
|
||||
).toBe('Automatic updates are disabled please check manually')
|
||||
})
|
||||
).toBe('Automatic updates are disabled please check manually');
|
||||
});
|
||||
|
||||
it('updates config and the UI when the checkbox is used to toggle', async () => {
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates input').checked
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
|
||||
aboutElement.querySelector('.about-auto-updates input').click()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
aboutElement.querySelector('.about-auto-updates input').click();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(false)
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates input').checked
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
.textContent
|
||||
).toBe('Automatic updates are disabled please check manually')
|
||||
).toBe('Automatic updates are disabled please check manually');
|
||||
|
||||
aboutElement.querySelector('.about-auto-updates input').click()
|
||||
await scheduler.getNextUpdatePromise()
|
||||
aboutElement.querySelector('.about-auto-updates input').click();
|
||||
await scheduler.getNextUpdatePromise();
|
||||
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(true)
|
||||
expect(atom.config.get('core.automaticallyUpdate')).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-auto-updates input').checked
|
||||
).toBe(true)
|
||||
).toBe(true);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector('.about-default-update-message')
|
||||
.textContent
|
||||
).toBe('Atom will check for updates automatically')
|
||||
})
|
||||
).toBe('Atom will check for updates automatically');
|
||||
});
|
||||
|
||||
describe('checking for updates', function () {
|
||||
describe('checking for updates', function() {
|
||||
afterEach(() => {
|
||||
this.updateView = null
|
||||
})
|
||||
this.updateView = null;
|
||||
});
|
||||
|
||||
it('checks for update when the about page is shown', () => {
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled()
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
|
||||
this.updateView = new UpdateView({
|
||||
updateManager: updateManager,
|
||||
availableVersion: '9999.0.0',
|
||||
viewUpdateReleaseNotes: () => {}
|
||||
})
|
||||
});
|
||||
|
||||
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled()
|
||||
})
|
||||
expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not check for update when the about page is shown and the update manager is not in the idle state', () => {
|
||||
atom.autoUpdater.getState.andReturn('downloading')
|
||||
updateManager.resetState()
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled()
|
||||
atom.autoUpdater.getState.andReturn('downloading');
|
||||
updateManager.resetState();
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
|
||||
this.updateView = new UpdateView({
|
||||
updateManager: updateManager,
|
||||
availableVersion: '9999.0.0',
|
||||
viewUpdateReleaseNotes: () => {}
|
||||
})
|
||||
});
|
||||
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled()
|
||||
})
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not check for update when the about page is shown and auto updates are turned off', () => {
|
||||
atom.config.set('core.automaticallyUpdate', false)
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled()
|
||||
atom.config.set('core.automaticallyUpdate', false);
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
|
||||
this.updateView = new UpdateView({
|
||||
updateManager: updateManager,
|
||||
availableVersion: '9999.0.0',
|
||||
viewUpdateReleaseNotes: () => {}
|
||||
})
|
||||
});
|
||||
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the About page is not open and an update is downloaded', () => {
|
||||
it('should display the new version when it is opened', async () => {
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0')
|
||||
MockUpdater.finishDownloadingUpdate('42.0.0');
|
||||
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
await atom.workspace.open('atom://about')
|
||||
aboutElement = workspaceElement.querySelector('.about')
|
||||
updateManager = main.model.state.updateManager
|
||||
scheduler = AboutView.getScheduler()
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
await atom.workspace.open('atom://about');
|
||||
aboutElement = workspaceElement.querySelector('.about');
|
||||
updateManager = main.model.state.updateManager;
|
||||
scheduler = AboutView.getScheduler();
|
||||
|
||||
expect(
|
||||
aboutElement.querySelector('.app-update-available-to-install')
|
||||
).toBeVisible()
|
||||
).toBeVisible();
|
||||
expect(
|
||||
aboutElement.querySelector(
|
||||
'.app-update-available-to-install .about-updates-version'
|
||||
).textContent
|
||||
).toBe('42.0.0')
|
||||
).toBe('42.0.0');
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').disabled
|
||||
).toBe(false)
|
||||
).toBe(false);
|
||||
expect(
|
||||
aboutElement.querySelector('.about-update-action-button').textContent
|
||||
).toBe('Restart and install')
|
||||
})
|
||||
})
|
||||
})
|
||||
).toBe('Restart and install');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,56 +1,56 @@
|
||||
/** @babel */
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
async enumerate () {
|
||||
async enumerate() {
|
||||
if (atom.inDevMode()) {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
|
||||
const duplicatePackages = []
|
||||
const names = atom.packages.getAvailablePackageNames()
|
||||
const duplicatePackages = [];
|
||||
const names = atom.packages.getAvailablePackageNames();
|
||||
for (let name of names) {
|
||||
if (atom.packages.isBundledPackage(name)) {
|
||||
const isDuplicatedPackage = await this.isInstalledAsCommunityPackage(
|
||||
name
|
||||
)
|
||||
);
|
||||
if (isDuplicatedPackage) {
|
||||
duplicatePackages.push(name)
|
||||
duplicatePackages.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duplicatePackages
|
||||
return duplicatePackages;
|
||||
},
|
||||
|
||||
async isInstalledAsCommunityPackage (name) {
|
||||
const availablePackagePaths = atom.packages.getPackageDirPaths()
|
||||
async isInstalledAsCommunityPackage(name) {
|
||||
const availablePackagePaths = atom.packages.getPackageDirPaths();
|
||||
|
||||
for (let packagePath of availablePackagePaths) {
|
||||
const candidate = path.join(packagePath, name)
|
||||
const candidate = path.join(packagePath, name);
|
||||
|
||||
if (fs.existsSync(candidate)) {
|
||||
const realPath = await this.realpath(candidate)
|
||||
const realPath = await this.realpath(candidate);
|
||||
if (realPath === candidate) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
|
||||
realpath (path) {
|
||||
realpath(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.realpath(path, function (error, realpath) {
|
||||
fs.realpath(path, function(error, realpath) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(realpath)
|
||||
resolve(realpath);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,19 +1,19 @@
|
||||
/** @babel */
|
||||
|
||||
const dalek = require('./dalek')
|
||||
const Grim = require('grim')
|
||||
const dalek = require('./dalek');
|
||||
const Grim = require('grim');
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
activate() {
|
||||
atom.packages.onDidActivateInitialPackages(async () => {
|
||||
const duplicates = await dalek.enumerate()
|
||||
const duplicates = await dalek.enumerate();
|
||||
for (let i = 0; i < duplicates.length; i++) {
|
||||
const duplicate = duplicates[i]
|
||||
const duplicate = duplicates[i];
|
||||
Grim.deprecate(
|
||||
`You have the core package "${duplicate}" installed as a community package. See https://github.com/atom/atom/blob/master/packages/dalek/README.md for how this causes problems and instructions on how to correct the situation.`,
|
||||
{ packageName: duplicate }
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
/** @babel */
|
||||
|
||||
const assert = require('assert')
|
||||
const fs = require('fs')
|
||||
const sinon = require('sinon')
|
||||
const path = require('path')
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const sinon = require('sinon');
|
||||
const path = require('path');
|
||||
|
||||
const dalek = require('../lib/dalek')
|
||||
const dalek = require('../lib/dalek');
|
||||
|
||||
describe('dalek', function () {
|
||||
describe('enumerate', function () {
|
||||
let availablePackages = {}
|
||||
let realPaths = {}
|
||||
let bundledPackages = []
|
||||
let packageDirPaths = []
|
||||
let sandbox = null
|
||||
describe('dalek', function() {
|
||||
describe('enumerate', function() {
|
||||
let availablePackages = {};
|
||||
let realPaths = {};
|
||||
let bundledPackages = [];
|
||||
let packageDirPaths = [];
|
||||
let sandbox = null;
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(function() {
|
||||
availablePackages = {
|
||||
'an-unduplicated-installed-package': path.join(
|
||||
'Users',
|
||||
@ -36,66 +36,68 @@ describe('dalek', function () {
|
||||
'node_modules',
|
||||
'unduplicated-package'
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
atom.devMode = false
|
||||
bundledPackages = ['duplicated-package', 'unduplicated-package']
|
||||
packageDirPaths = [path.join('Users', 'username', '.atom', 'packages')]
|
||||
sandbox = sinon.sandbox.create()
|
||||
atom.devMode = false;
|
||||
bundledPackages = ['duplicated-package', 'unduplicated-package'];
|
||||
packageDirPaths = [path.join('Users', 'username', '.atom', 'packages')];
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox
|
||||
.stub(dalek, 'realpath')
|
||||
.callsFake(filePath => Promise.resolve(realPaths[filePath] || filePath))
|
||||
.callsFake(filePath =>
|
||||
Promise.resolve(realPaths[filePath] || filePath)
|
||||
);
|
||||
sandbox.stub(atom.packages, 'isBundledPackage').callsFake(packageName => {
|
||||
return bundledPackages.includes(packageName)
|
||||
})
|
||||
return bundledPackages.includes(packageName);
|
||||
});
|
||||
sandbox
|
||||
.stub(atom.packages, 'getAvailablePackageNames')
|
||||
.callsFake(() => Object.keys(availablePackages))
|
||||
.callsFake(() => Object.keys(availablePackages));
|
||||
sandbox.stub(atom.packages, 'getPackageDirPaths').callsFake(() => {
|
||||
return packageDirPaths
|
||||
})
|
||||
return packageDirPaths;
|
||||
});
|
||||
sandbox.stub(fs, 'existsSync').callsFake(candidate => {
|
||||
return (
|
||||
Object.values(availablePackages).includes(candidate) &&
|
||||
!candidate.includes(atom.getLoadSettings().resourcePath)
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore()
|
||||
})
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('returns a list of duplicate names', async function () {
|
||||
assert.deepEqual(await dalek.enumerate(), ['duplicated-package'])
|
||||
})
|
||||
it('returns a list of duplicate names', async function() {
|
||||
assert.deepEqual(await dalek.enumerate(), ['duplicated-package']);
|
||||
});
|
||||
|
||||
describe('when in dev mode', function () {
|
||||
beforeEach(function () {
|
||||
atom.devMode = true
|
||||
})
|
||||
describe('when in dev mode', function() {
|
||||
beforeEach(function() {
|
||||
atom.devMode = true;
|
||||
});
|
||||
|
||||
it('always returns an empty list', async function () {
|
||||
assert.deepEqual(await dalek.enumerate(), [])
|
||||
})
|
||||
})
|
||||
it('always returns an empty list', async function() {
|
||||
assert.deepEqual(await dalek.enumerate(), []);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a package is symlinked into the package directory', async function () {
|
||||
beforeEach(function () {
|
||||
const realPath = path.join('Users', 'username', 'duplicated-package')
|
||||
describe('when a package is symlinked into the package directory', async function() {
|
||||
beforeEach(function() {
|
||||
const realPath = path.join('Users', 'username', 'duplicated-package');
|
||||
const packagePath = path.join(
|
||||
'Users',
|
||||
'username',
|
||||
'.atom',
|
||||
'packages',
|
||||
'duplicated-package'
|
||||
)
|
||||
realPaths[packagePath] = realPath
|
||||
})
|
||||
);
|
||||
realPaths[packagePath] = realPath;
|
||||
});
|
||||
|
||||
it('is not included in the list of duplicate names', async function () {
|
||||
assert.deepEqual(await dalek.enumerate(), [])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('is not included in the list of duplicate names', async function() {
|
||||
assert.deepEqual(await dalek.enumerate(), []);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,2 +1,2 @@
|
||||
const createRunner = require('atom-mocha-test-runner').createRunner
|
||||
module.exports = createRunner({ testSuffixes: ['test.js'] })
|
||||
const createRunner = require('atom-mocha-test-runner').createRunner;
|
||||
module.exports = createRunner({ testSuffixes: ['test.js'] });
|
||||
|
@ -1,88 +1,88 @@
|
||||
/** @babel */
|
||||
/** @jsx etch.dom */
|
||||
|
||||
import _ from 'underscore-plus'
|
||||
import { CompositeDisposable } from 'atom'
|
||||
import etch from 'etch'
|
||||
import fs from 'fs-plus'
|
||||
import Grim from 'grim'
|
||||
import marked from 'marked'
|
||||
import path from 'path'
|
||||
import shell from 'shell'
|
||||
import _ from 'underscore-plus';
|
||||
import { CompositeDisposable } from 'atom';
|
||||
import etch from 'etch';
|
||||
import fs from 'fs-plus';
|
||||
import Grim from 'grim';
|
||||
import marked from 'marked';
|
||||
import path from 'path';
|
||||
import shell from 'shell';
|
||||
|
||||
export default class DeprecationCopView {
|
||||
constructor ({ uri }) {
|
||||
this.uri = uri
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
constructor({ uri }) {
|
||||
this.uri = uri;
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
this.subscriptions.add(
|
||||
Grim.on('updated', () => {
|
||||
etch.update(this)
|
||||
etch.update(this);
|
||||
})
|
||||
)
|
||||
);
|
||||
// TODO: Remove conditional when the new StyleManager deprecation APIs reach stable.
|
||||
if (atom.styles.onDidUpdateDeprecations) {
|
||||
this.subscriptions.add(
|
||||
atom.styles.onDidUpdateDeprecations(() => {
|
||||
etch.update(this)
|
||||
etch.update(this);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
etch.initialize(this)
|
||||
etch.initialize(this);
|
||||
this.subscriptions.add(
|
||||
atom.commands.add(this.element, {
|
||||
'core:move-up': () => {
|
||||
this.scrollUp()
|
||||
this.scrollUp();
|
||||
},
|
||||
'core:move-down': () => {
|
||||
this.scrollDown()
|
||||
this.scrollDown();
|
||||
},
|
||||
'core:page-up': () => {
|
||||
this.pageUp()
|
||||
this.pageUp();
|
||||
},
|
||||
'core:page-down': () => {
|
||||
this.pageDown()
|
||||
this.pageDown();
|
||||
},
|
||||
'core:move-to-top': () => {
|
||||
this.scrollToTop()
|
||||
this.scrollToTop();
|
||||
},
|
||||
'core:move-to-bottom': () => {
|
||||
this.scrollToBottom()
|
||||
this.scrollToBottom();
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
serialize () {
|
||||
serialize() {
|
||||
return {
|
||||
deserializer: this.constructor.name,
|
||||
uri: this.getURI(),
|
||||
version: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.subscriptions.dispose()
|
||||
return etch.destroy(this)
|
||||
destroy() {
|
||||
this.subscriptions.dispose();
|
||||
return etch.destroy(this);
|
||||
}
|
||||
|
||||
update () {
|
||||
return etch.update(this)
|
||||
update() {
|
||||
return etch.update(this);
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className='deprecation-cop pane-item native-key-bindings'
|
||||
tabIndex='-1'
|
||||
className="deprecation-cop pane-item native-key-bindings"
|
||||
tabIndex="-1"
|
||||
>
|
||||
<div className='panel'>
|
||||
<div className='padded deprecation-overview'>
|
||||
<div className='pull-right btn-group'>
|
||||
<div className="panel">
|
||||
<div className="padded deprecation-overview">
|
||||
<div className="pull-right btn-group">
|
||||
<button
|
||||
className='btn btn-primary check-for-update'
|
||||
className="btn btn-primary check-for-update"
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.checkForUpdates()
|
||||
event.preventDefault();
|
||||
this.checkForUpdates();
|
||||
}}
|
||||
>
|
||||
Check for Updates
|
||||
@ -90,53 +90,53 @@ export default class DeprecationCopView {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='panel-heading'>
|
||||
<div className="panel-heading">
|
||||
<span>Deprecated calls</span>
|
||||
</div>
|
||||
<ul className='list-tree has-collapsable-children'>
|
||||
<ul className="list-tree has-collapsable-children">
|
||||
{this.renderDeprecatedCalls()}
|
||||
</ul>
|
||||
|
||||
<div className='panel-heading'>
|
||||
<div className="panel-heading">
|
||||
<span>Deprecated selectors</span>
|
||||
</div>
|
||||
<ul className='selectors list-tree has-collapsable-children'>
|
||||
<ul className="selectors list-tree has-collapsable-children">
|
||||
{this.renderDeprecatedSelectors()}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderDeprecatedCalls () {
|
||||
const deprecationsByPackageName = this.getDeprecatedCallsByPackageName()
|
||||
const packageNames = Object.keys(deprecationsByPackageName)
|
||||
renderDeprecatedCalls() {
|
||||
const deprecationsByPackageName = this.getDeprecatedCallsByPackageName();
|
||||
const packageNames = Object.keys(deprecationsByPackageName);
|
||||
if (packageNames.length === 0) {
|
||||
return <li className='list-item'>No deprecated calls</li>
|
||||
return <li className="list-item">No deprecated calls</li>;
|
||||
} else {
|
||||
return packageNames.sort().map(packageName => (
|
||||
<li className='deprecation list-nested-item collapsed'>
|
||||
<li className="deprecation list-nested-item collapsed">
|
||||
<div
|
||||
className='deprecation-info list-item'
|
||||
className="deprecation-info list-item"
|
||||
onclick={event =>
|
||||
event.target.parentElement.classList.toggle('collapsed')
|
||||
}
|
||||
>
|
||||
<span className='text-highlight'>{packageName || 'atom core'}</span>
|
||||
<span className="text-highlight">{packageName || 'atom core'}</span>
|
||||
<span>{` (${_.pluralize(
|
||||
deprecationsByPackageName[packageName].length,
|
||||
'deprecation'
|
||||
)})`}</span>
|
||||
</div>
|
||||
|
||||
<ul className='list'>
|
||||
<ul className="list">
|
||||
{this.renderPackageActionsIfNeeded(packageName)}
|
||||
{deprecationsByPackageName[packageName].map(
|
||||
({ deprecation, stack }) => (
|
||||
<li className='list-item deprecation-detail'>
|
||||
<span className='text-warning icon icon-alert' />
|
||||
<li className="list-item deprecation-detail">
|
||||
<span className="text-warning icon icon-alert" />
|
||||
<div
|
||||
className='list-item deprecation-message'
|
||||
className="list-item deprecation-message"
|
||||
innerHTML={marked(deprecation.getMessage())}
|
||||
/>
|
||||
{this.renderIssueURLIfNeeded(
|
||||
@ -144,17 +144,17 @@ export default class DeprecationCopView {
|
||||
deprecation,
|
||||
this.buildIssueURL(packageName, deprecation, stack)
|
||||
)}
|
||||
<div className='stack-trace'>
|
||||
<div className="stack-trace">
|
||||
{stack.map(({ functionName, location }) => (
|
||||
<div className='stack-line'>
|
||||
<div className="stack-line">
|
||||
<span>{functionName}</span>
|
||||
<span> - </span>
|
||||
<a
|
||||
className='stack-line-location'
|
||||
className="stack-line-location"
|
||||
href={location}
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.openLocation(location)
|
||||
event.preventDefault();
|
||||
this.openLocation(location);
|
||||
}}
|
||||
>
|
||||
{location}
|
||||
@ -167,56 +167,56 @@ export default class DeprecationCopView {
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
renderDeprecatedSelectors () {
|
||||
const deprecationsByPackageName = this.getDeprecatedSelectorsByPackageName()
|
||||
const packageNames = Object.keys(deprecationsByPackageName)
|
||||
renderDeprecatedSelectors() {
|
||||
const deprecationsByPackageName = this.getDeprecatedSelectorsByPackageName();
|
||||
const packageNames = Object.keys(deprecationsByPackageName);
|
||||
if (packageNames.length === 0) {
|
||||
return <li className='list-item'>No deprecated selectors</li>
|
||||
return <li className="list-item">No deprecated selectors</li>;
|
||||
} else {
|
||||
return packageNames.map(packageName => (
|
||||
<li className='deprecation list-nested-item collapsed'>
|
||||
<li className="deprecation list-nested-item collapsed">
|
||||
<div
|
||||
className='deprecation-info list-item'
|
||||
className="deprecation-info list-item"
|
||||
onclick={event =>
|
||||
event.target.parentElement.classList.toggle('collapsed')
|
||||
}
|
||||
>
|
||||
<span className='text-highlight'>{packageName}</span>
|
||||
<span className="text-highlight">{packageName}</span>
|
||||
</div>
|
||||
|
||||
<ul className='list'>
|
||||
<ul className="list">
|
||||
{this.renderPackageActionsIfNeeded(packageName)}
|
||||
{deprecationsByPackageName[packageName].map(
|
||||
({ packagePath, sourcePath, deprecation }) => {
|
||||
const relativeSourcePath = path.relative(
|
||||
packagePath,
|
||||
sourcePath
|
||||
)
|
||||
const issueTitle = `Deprecated selector in \`${relativeSourcePath}\``
|
||||
);
|
||||
const issueTitle = `Deprecated selector in \`${relativeSourcePath}\``;
|
||||
const issueBody = `In \`${relativeSourcePath}\`: \n\n${
|
||||
deprecation.message
|
||||
}`
|
||||
}`;
|
||||
return (
|
||||
<li className='list-item source-file'>
|
||||
<li className="list-item source-file">
|
||||
<a
|
||||
className='source-url'
|
||||
className="source-url"
|
||||
href={sourcePath}
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.openLocation(sourcePath)
|
||||
event.preventDefault();
|
||||
this.openLocation(sourcePath);
|
||||
}}
|
||||
>
|
||||
{relativeSourcePath}
|
||||
</a>
|
||||
<ul className='list'>
|
||||
<li className='list-item deprecation-detail'>
|
||||
<span className='text-warning icon icon-alert' />
|
||||
<ul className="list">
|
||||
<li className="list-item deprecation-detail">
|
||||
<span className="text-warning icon icon-alert" />
|
||||
<div
|
||||
className='list-item deprecation-message'
|
||||
className="list-item deprecation-message"
|
||||
innerHTML={marked(deprecation.message)}
|
||||
/>
|
||||
{this.renderSelectorIssueURLIfNeeded(
|
||||
@ -227,138 +227,138 @@ export default class DeprecationCopView {
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
);
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
renderPackageActionsIfNeeded (packageName) {
|
||||
renderPackageActionsIfNeeded(packageName) {
|
||||
if (packageName && atom.packages.getLoadedPackage(packageName)) {
|
||||
return (
|
||||
<div className='padded'>
|
||||
<div className='btn-group'>
|
||||
<div className="padded">
|
||||
<div className="btn-group">
|
||||
<button
|
||||
className='btn check-for-update'
|
||||
className="btn check-for-update"
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.checkForUpdates()
|
||||
event.preventDefault();
|
||||
this.checkForUpdates();
|
||||
}}
|
||||
>
|
||||
Check for Update
|
||||
</button>
|
||||
<button
|
||||
className='btn disable-package'
|
||||
className="btn disable-package"
|
||||
data-package-name={packageName}
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.disablePackage(packageName)
|
||||
event.preventDefault();
|
||||
this.disablePackage(packageName);
|
||||
}}
|
||||
>
|
||||
Disable Package
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
encodeURI (str) {
|
||||
encodeURI(str) {
|
||||
return encodeURI(str)
|
||||
.replace(/#/g, '%23')
|
||||
.replace(/;/g, '%3B')
|
||||
.replace(/%20/g, '+')
|
||||
.replace(/%20/g, '+');
|
||||
}
|
||||
|
||||
renderSelectorIssueURLIfNeeded (packageName, issueTitle, issueBody) {
|
||||
const repoURL = this.getRepoURL(packageName)
|
||||
renderSelectorIssueURLIfNeeded(packageName, issueTitle, issueBody) {
|
||||
const repoURL = this.getRepoURL(packageName);
|
||||
if (repoURL) {
|
||||
const issueURL = `${repoURL}/issues/new?title=${this.encodeURI(
|
||||
issueTitle
|
||||
)}&body=${this.encodeURI(issueBody)}`
|
||||
)}&body=${this.encodeURI(issueBody)}`;
|
||||
return (
|
||||
<div className='btn-toolbar'>
|
||||
<div className="btn-toolbar">
|
||||
<button
|
||||
className='btn issue-url'
|
||||
className="btn issue-url"
|
||||
data-issue-title={issueTitle}
|
||||
data-repo-url={repoURL}
|
||||
data-issue-url={issueURL}
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.openIssueURL(repoURL, issueURL, issueTitle)
|
||||
event.preventDefault();
|
||||
this.openIssueURL(repoURL, issueURL, issueTitle);
|
||||
}}
|
||||
>
|
||||
Report Issue
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
renderIssueURLIfNeeded (packageName, deprecation, issueURL) {
|
||||
renderIssueURLIfNeeded(packageName, deprecation, issueURL) {
|
||||
if (packageName && issueURL) {
|
||||
const repoURL = this.getRepoURL(packageName)
|
||||
const issueTitle = `${deprecation.getOriginName()} is deprecated.`
|
||||
const repoURL = this.getRepoURL(packageName);
|
||||
const issueTitle = `${deprecation.getOriginName()} is deprecated.`;
|
||||
return (
|
||||
<div className='btn-toolbar'>
|
||||
<div className="btn-toolbar">
|
||||
<button
|
||||
className='btn issue-url'
|
||||
className="btn issue-url"
|
||||
data-issue-title={issueTitle}
|
||||
data-repo-url={repoURL}
|
||||
data-issue-url={issueURL}
|
||||
onclick={event => {
|
||||
event.preventDefault()
|
||||
this.openIssueURL(repoURL, issueURL, issueTitle)
|
||||
event.preventDefault();
|
||||
this.openIssueURL(repoURL, issueURL, issueTitle);
|
||||
}}
|
||||
>
|
||||
Report Issue
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
buildIssueURL (packageName, deprecation, stack) {
|
||||
const repoURL = this.getRepoURL(packageName)
|
||||
buildIssueURL(packageName, deprecation, stack) {
|
||||
const repoURL = this.getRepoURL(packageName);
|
||||
if (repoURL) {
|
||||
const title = `${deprecation.getOriginName()} is deprecated.`
|
||||
const title = `${deprecation.getOriginName()} is deprecated.`;
|
||||
const stacktrace = stack
|
||||
.map(({ functionName, location }) => `${functionName} (${location})`)
|
||||
.join('\n')
|
||||
const body = `${deprecation.getMessage()}\n\`\`\`\n${stacktrace}\n\`\`\``
|
||||
.join('\n');
|
||||
const body = `${deprecation.getMessage()}\n\`\`\`\n${stacktrace}\n\`\`\``;
|
||||
return `${repoURL}/issues/new?title=${encodeURI(title)}&body=${encodeURI(
|
||||
body
|
||||
)}`
|
||||
)}`;
|
||||
} else {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async openIssueURL (repoURL, issueURL, issueTitle) {
|
||||
const issue = await this.findSimilarIssue(repoURL, issueTitle)
|
||||
async openIssueURL(repoURL, issueURL, issueTitle) {
|
||||
const issue = await this.findSimilarIssue(repoURL, issueTitle);
|
||||
if (issue) {
|
||||
shell.openExternal(issue.html_url)
|
||||
shell.openExternal(issue.html_url);
|
||||
} else if (process.platform === 'win32') {
|
||||
// Windows will not launch URLs greater than ~2000 bytes so we need to shrink it
|
||||
shell.openExternal((await this.shortenURL(issueURL)) || issueURL)
|
||||
shell.openExternal((await this.shortenURL(issueURL)) || issueURL);
|
||||
} else {
|
||||
shell.openExternal(issueURL)
|
||||
shell.openExternal(issueURL);
|
||||
}
|
||||
}
|
||||
|
||||
async findSimilarIssue (repoURL, issueTitle) {
|
||||
const url = 'https://api.github.com/search/issues'
|
||||
const repo = repoURL.replace(/http(s)?:\/\/(\d+\.)?github.com\//gi, '')
|
||||
const query = `${issueTitle} repo:${repo}`
|
||||
async findSimilarIssue(repoURL, issueTitle) {
|
||||
const url = 'https://api.github.com/search/issues';
|
||||
const repo = repoURL.replace(/http(s)?:\/\/(\d+\.)?github.com\//gi, '');
|
||||
const query = `${issueTitle} repo:${repo}`;
|
||||
const response = await window.fetch(
|
||||
`${url}?q=${encodeURI(query)}&sort=created`,
|
||||
{
|
||||
@ -368,45 +368,45 @@ export default class DeprecationCopView {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
const data = await response.json();
|
||||
if (data.items) {
|
||||
const issues = {}
|
||||
const issues = {};
|
||||
for (const issue of data.items) {
|
||||
if (issue.title.includes(issueTitle) && !issues[issue.state]) {
|
||||
issues[issue.state] = issue
|
||||
issues[issue.state] = issue;
|
||||
}
|
||||
}
|
||||
|
||||
return issues.open || issues.closed
|
||||
return issues.open || issues.closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async shortenURL (url) {
|
||||
let encodedUrl = encodeURIComponent(url).substr(0, 5000) // is.gd has 5000 char limit
|
||||
async shortenURL(url) {
|
||||
let encodedUrl = encodeURIComponent(url).substr(0, 5000); // is.gd has 5000 char limit
|
||||
let incompletePercentEncoding = encodedUrl.indexOf(
|
||||
'%',
|
||||
encodedUrl.length - 2
|
||||
)
|
||||
);
|
||||
if (incompletePercentEncoding >= 0) {
|
||||
// Handle an incomplete % encoding cut-off
|
||||
encodedUrl = encodedUrl.substr(0, incompletePercentEncoding)
|
||||
encodedUrl = encodedUrl.substr(0, incompletePercentEncoding);
|
||||
}
|
||||
|
||||
let result = await fetch('https://is.gd/create.php?format=simple', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `url=${encodedUrl}`
|
||||
})
|
||||
});
|
||||
|
||||
return result.text()
|
||||
return result.text();
|
||||
}
|
||||
|
||||
getRepoURL (packageName) {
|
||||
const loadedPackage = atom.packages.getLoadedPackage(packageName)
|
||||
getRepoURL(packageName) {
|
||||
const loadedPackage = atom.packages.getLoadedPackage(packageName);
|
||||
if (
|
||||
loadedPackage &&
|
||||
loadedPackage.metadata &&
|
||||
@ -414,171 +414,171 @@ export default class DeprecationCopView {
|
||||
) {
|
||||
const url =
|
||||
loadedPackage.metadata.repository.url ||
|
||||
loadedPackage.metadata.repository
|
||||
return url.replace(/\.git$/, '')
|
||||
loadedPackage.metadata.repository;
|
||||
return url.replace(/\.git$/, '');
|
||||
} else {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getDeprecatedCallsByPackageName () {
|
||||
const deprecatedCalls = Grim.getDeprecations()
|
||||
deprecatedCalls.sort((a, b) => b.getCallCount() - a.getCallCount())
|
||||
const deprecatedCallsByPackageName = {}
|
||||
getDeprecatedCallsByPackageName() {
|
||||
const deprecatedCalls = Grim.getDeprecations();
|
||||
deprecatedCalls.sort((a, b) => b.getCallCount() - a.getCallCount());
|
||||
const deprecatedCallsByPackageName = {};
|
||||
for (const deprecation of deprecatedCalls) {
|
||||
const stacks = deprecation.getStacks()
|
||||
stacks.sort((a, b) => b.callCount - a.callCount)
|
||||
const stacks = deprecation.getStacks();
|
||||
stacks.sort((a, b) => b.callCount - a.callCount);
|
||||
for (const stack of stacks) {
|
||||
let packageName = null
|
||||
let packageName = null;
|
||||
if (stack.metadata && stack.metadata.packageName) {
|
||||
packageName = stack.metadata.packageName
|
||||
packageName = stack.metadata.packageName;
|
||||
} else {
|
||||
packageName = (this.getPackageName(stack) || '').toLowerCase()
|
||||
packageName = (this.getPackageName(stack) || '').toLowerCase();
|
||||
}
|
||||
|
||||
deprecatedCallsByPackageName[packageName] =
|
||||
deprecatedCallsByPackageName[packageName] || []
|
||||
deprecatedCallsByPackageName[packageName].push({ deprecation, stack })
|
||||
deprecatedCallsByPackageName[packageName] || [];
|
||||
deprecatedCallsByPackageName[packageName].push({ deprecation, stack });
|
||||
}
|
||||
}
|
||||
return deprecatedCallsByPackageName
|
||||
return deprecatedCallsByPackageName;
|
||||
}
|
||||
|
||||
getDeprecatedSelectorsByPackageName () {
|
||||
const deprecatedSelectorsByPackageName = {}
|
||||
getDeprecatedSelectorsByPackageName() {
|
||||
const deprecatedSelectorsByPackageName = {};
|
||||
if (atom.styles.getDeprecations) {
|
||||
const deprecatedSelectorsBySourcePath = atom.styles.getDeprecations()
|
||||
const deprecatedSelectorsBySourcePath = atom.styles.getDeprecations();
|
||||
for (const sourcePath of Object.keys(deprecatedSelectorsBySourcePath)) {
|
||||
const deprecation = deprecatedSelectorsBySourcePath[sourcePath]
|
||||
const components = sourcePath.split(path.sep)
|
||||
const packagesComponentIndex = components.indexOf('packages')
|
||||
let packageName = null
|
||||
let packagePath = null
|
||||
const deprecation = deprecatedSelectorsBySourcePath[sourcePath];
|
||||
const components = sourcePath.split(path.sep);
|
||||
const packagesComponentIndex = components.indexOf('packages');
|
||||
let packageName = null;
|
||||
let packagePath = null;
|
||||
if (packagesComponentIndex === -1) {
|
||||
packageName = 'Other' // could be Atom Core or the personal style sheet
|
||||
packagePath = ''
|
||||
packageName = 'Other'; // could be Atom Core or the personal style sheet
|
||||
packagePath = '';
|
||||
} else {
|
||||
packageName = components[packagesComponentIndex + 1]
|
||||
packageName = components[packagesComponentIndex + 1];
|
||||
packagePath = components
|
||||
.slice(0, packagesComponentIndex + 1)
|
||||
.join(path.sep)
|
||||
.join(path.sep);
|
||||
}
|
||||
|
||||
deprecatedSelectorsByPackageName[packageName] =
|
||||
deprecatedSelectorsByPackageName[packageName] || []
|
||||
deprecatedSelectorsByPackageName[packageName] || [];
|
||||
deprecatedSelectorsByPackageName[packageName].push({
|
||||
packagePath,
|
||||
sourcePath,
|
||||
deprecation
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return deprecatedSelectorsByPackageName
|
||||
return deprecatedSelectorsByPackageName;
|
||||
}
|
||||
|
||||
getPackageName (stack) {
|
||||
const packagePaths = this.getPackagePathsByPackageName()
|
||||
getPackageName(stack) {
|
||||
const packagePaths = this.getPackagePathsByPackageName();
|
||||
for (const [packageName, packagePath] of packagePaths) {
|
||||
if (
|
||||
packagePath.includes('.atom/dev/packages') ||
|
||||
packagePath.includes('.atom/packages')
|
||||
) {
|
||||
packagePaths.set(packageName, fs.absolute(packagePath))
|
||||
packagePaths.set(packageName, fs.absolute(packagePath));
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 1; i < stack.length; i++) {
|
||||
const { fileName } = stack[i]
|
||||
const { fileName } = stack[i];
|
||||
|
||||
// Empty when it was run from the dev console
|
||||
if (!fileName) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// Continue to next stack entry if call is in node_modules
|
||||
if (fileName.includes(`${path.sep}node_modules${path.sep}`)) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const [packageName, packagePath] of packagePaths) {
|
||||
const relativePath = path.relative(packagePath, fileName)
|
||||
const relativePath = path.relative(packagePath, fileName);
|
||||
if (!/^\.\./.test(relativePath)) {
|
||||
return packageName
|
||||
return packageName;
|
||||
}
|
||||
}
|
||||
|
||||
if (atom.getUserInitScriptPath() === fileName) {
|
||||
return `Your local ${path.basename(fileName)} file`
|
||||
return `Your local ${path.basename(fileName)} file`;
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
getPackagePathsByPackageName () {
|
||||
getPackagePathsByPackageName() {
|
||||
if (this.packagePathsByPackageName) {
|
||||
return this.packagePathsByPackageName
|
||||
return this.packagePathsByPackageName;
|
||||
} else {
|
||||
this.packagePathsByPackageName = new Map()
|
||||
this.packagePathsByPackageName = new Map();
|
||||
for (const pack of atom.packages.getLoadedPackages()) {
|
||||
this.packagePathsByPackageName.set(pack.name, pack.path)
|
||||
this.packagePathsByPackageName.set(pack.name, pack.path);
|
||||
}
|
||||
return this.packagePathsByPackageName
|
||||
return this.packagePathsByPackageName;
|
||||
}
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
atom.workspace.open('atom://config/updates')
|
||||
checkForUpdates() {
|
||||
atom.workspace.open('atom://config/updates');
|
||||
}
|
||||
|
||||
disablePackage (packageName) {
|
||||
disablePackage(packageName) {
|
||||
if (packageName) {
|
||||
atom.packages.disablePackage(packageName)
|
||||
atom.packages.disablePackage(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
openLocation (location) {
|
||||
let pathToOpen = location.replace('file://', '')
|
||||
openLocation(location) {
|
||||
let pathToOpen = location.replace('file://', '');
|
||||
if (process.platform === 'win32') {
|
||||
pathToOpen = pathToOpen.replace(/^\//, '')
|
||||
pathToOpen = pathToOpen.replace(/^\//, '');
|
||||
}
|
||||
atom.open({ pathsToOpen: [pathToOpen] })
|
||||
atom.open({ pathsToOpen: [pathToOpen] });
|
||||
}
|
||||
|
||||
getURI () {
|
||||
return this.uri
|
||||
getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
getTitle () {
|
||||
return 'Deprecation Cop'
|
||||
getTitle() {
|
||||
return 'Deprecation Cop';
|
||||
}
|
||||
|
||||
getIconName () {
|
||||
return 'alert'
|
||||
getIconName() {
|
||||
return 'alert';
|
||||
}
|
||||
|
||||
scrollUp () {
|
||||
this.element.scrollTop -= document.body.offsetHeight / 20
|
||||
scrollUp() {
|
||||
this.element.scrollTop -= document.body.offsetHeight / 20;
|
||||
}
|
||||
|
||||
scrollDown () {
|
||||
this.element.scrollTop += document.body.offsetHeight / 20
|
||||
scrollDown() {
|
||||
this.element.scrollTop += document.body.offsetHeight / 20;
|
||||
}
|
||||
|
||||
pageUp () {
|
||||
this.element.scrollTop -= this.element.offsetHeight
|
||||
pageUp() {
|
||||
this.element.scrollTop -= this.element.offsetHeight;
|
||||
}
|
||||
|
||||
pageDown () {
|
||||
this.element.scrollTop += this.element.offsetHeight
|
||||
pageDown() {
|
||||
this.element.scrollTop += this.element.offsetHeight;
|
||||
}
|
||||
|
||||
scrollToTop () {
|
||||
this.element.scrollTop = 0
|
||||
scrollToTop() {
|
||||
this.element.scrollTop = 0;
|
||||
}
|
||||
|
||||
scrollToBottom () {
|
||||
this.element.scrollTop = this.element.scrollHeight
|
||||
scrollToBottom() {
|
||||
this.element.scrollTop = this.element.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,54 @@
|
||||
const { Disposable, CompositeDisposable } = require('atom')
|
||||
const DeprecationCopView = require('./deprecation-cop-view')
|
||||
const DeprecationCopStatusBarView = require('./deprecation-cop-status-bar-view')
|
||||
const ViewURI = 'atom://deprecation-cop'
|
||||
const { Disposable, CompositeDisposable } = require('atom');
|
||||
const DeprecationCopView = require('./deprecation-cop-view');
|
||||
const DeprecationCopStatusBarView = require('./deprecation-cop-status-bar-view');
|
||||
const ViewURI = 'atom://deprecation-cop';
|
||||
|
||||
class DeprecationCopPackage {
|
||||
activate () {
|
||||
this.disposables = new CompositeDisposable()
|
||||
activate() {
|
||||
this.disposables = new CompositeDisposable();
|
||||
this.disposables.add(
|
||||
atom.workspace.addOpener(uri => {
|
||||
if (uri === ViewURI) {
|
||||
return this.deserializeDeprecationCopView({ uri })
|
||||
return this.deserializeDeprecationCopView({ uri });
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
this.disposables.add(
|
||||
atom.commands.add('atom-workspace', 'deprecation-cop:view', () => {
|
||||
atom.workspace.open(ViewURI)
|
||||
atom.workspace.open(ViewURI);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
deactivate () {
|
||||
this.disposables.dispose()
|
||||
const pane = atom.workspace.paneForURI(ViewURI)
|
||||
deactivate() {
|
||||
this.disposables.dispose();
|
||||
const pane = atom.workspace.paneForURI(ViewURI);
|
||||
if (pane) {
|
||||
pane.destroyItem(pane.itemForURI(ViewURI))
|
||||
pane.destroyItem(pane.itemForURI(ViewURI));
|
||||
}
|
||||
}
|
||||
|
||||
deserializeDeprecationCopView (state) {
|
||||
return new DeprecationCopView(state)
|
||||
deserializeDeprecationCopView(state) {
|
||||
return new DeprecationCopView(state);
|
||||
}
|
||||
|
||||
consumeStatusBar (statusBar) {
|
||||
const statusBarView = new DeprecationCopStatusBarView()
|
||||
consumeStatusBar(statusBar) {
|
||||
const statusBarView = new DeprecationCopStatusBarView();
|
||||
const statusBarTile = statusBar.addRightTile({
|
||||
item: statusBarView,
|
||||
priority: 150
|
||||
})
|
||||
});
|
||||
this.disposables.add(
|
||||
new Disposable(() => {
|
||||
statusBarView.destroy()
|
||||
statusBarView.destroy();
|
||||
})
|
||||
)
|
||||
);
|
||||
this.disposables.add(
|
||||
new Disposable(() => {
|
||||
statusBarTile.destroy()
|
||||
statusBarTile.destroy();
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new DeprecationCopPackage()
|
||||
module.exports = new DeprecationCopPackage();
|
||||
|
@ -1,31 +1,31 @@
|
||||
const fs = require('fs-plus')
|
||||
const path = require('path')
|
||||
const Watcher = require('./watcher')
|
||||
const fs = require('fs-plus');
|
||||
const path = require('path');
|
||||
const Watcher = require('./watcher');
|
||||
|
||||
module.exports = class BaseThemeWatcher extends Watcher {
|
||||
constructor () {
|
||||
super()
|
||||
constructor() {
|
||||
super();
|
||||
this.stylesheetsPath = path.dirname(
|
||||
atom.themes.resolveStylesheet('../static/atom.less')
|
||||
)
|
||||
this.watch()
|
||||
);
|
||||
this.watch();
|
||||
}
|
||||
|
||||
watch () {
|
||||
watch() {
|
||||
const filePaths = fs
|
||||
.readdirSync(this.stylesheetsPath)
|
||||
.filter(filePath => path.extname(filePath).includes('less'))
|
||||
.filter(filePath => path.extname(filePath).includes('less'));
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
this.watchFile(path.join(this.stylesheetsPath, filePath))
|
||||
this.watchFile(path.join(this.stylesheetsPath, filePath));
|
||||
}
|
||||
}
|
||||
|
||||
loadStylesheet () {
|
||||
this.loadAllStylesheets()
|
||||
loadStylesheet() {
|
||||
this.loadAllStylesheets();
|
||||
}
|
||||
|
||||
loadAllStylesheets () {
|
||||
atom.themes.reloadBaseStylesheets()
|
||||
loadAllStylesheets() {
|
||||
atom.themes.reloadBaseStylesheets();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,30 +1,30 @@
|
||||
module.exports = {
|
||||
activate (state) {
|
||||
if (!atom.inDevMode() || atom.inSpecMode()) return
|
||||
activate(state) {
|
||||
if (!atom.inDevMode() || atom.inSpecMode()) return;
|
||||
|
||||
if (atom.packages.hasActivatedInitialPackages()) {
|
||||
this.startWatching()
|
||||
this.startWatching();
|
||||
} else {
|
||||
this.activatedDisposable = atom.packages.onDidActivateInitialPackages(
|
||||
() => this.startWatching()
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
if (this.activatedDisposable) this.activatedDisposable.dispose()
|
||||
if (this.commandDisposable) this.commandDisposable.dispose()
|
||||
if (this.uiWatcher) this.uiWatcher.destroy()
|
||||
deactivate() {
|
||||
if (this.activatedDisposable) this.activatedDisposable.dispose();
|
||||
if (this.commandDisposable) this.commandDisposable.dispose();
|
||||
if (this.uiWatcher) this.uiWatcher.destroy();
|
||||
},
|
||||
|
||||
startWatching () {
|
||||
const UIWatcher = require('./ui-watcher')
|
||||
this.uiWatcher = new UIWatcher({ themeManager: atom.themes })
|
||||
startWatching() {
|
||||
const UIWatcher = require('./ui-watcher');
|
||||
this.uiWatcher = new UIWatcher({ themeManager: atom.themes });
|
||||
this.commandDisposable = atom.commands.add(
|
||||
'atom-workspace',
|
||||
'dev-live-reload:reload-all',
|
||||
() => this.uiWatcher.reloadAll()
|
||||
)
|
||||
if (this.activatedDisposable) this.activatedDisposable.dispose()
|
||||
);
|
||||
if (this.activatedDisposable) this.activatedDisposable.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,49 +1,50 @@
|
||||
const fs = require('fs-plus')
|
||||
const fs = require('fs-plus');
|
||||
|
||||
const Watcher = require('./watcher')
|
||||
const Watcher = require('./watcher');
|
||||
|
||||
module.exports = class PackageWatcher extends Watcher {
|
||||
static supportsPackage (pack, type) {
|
||||
if (pack.getType() === type && pack.getStylesheetPaths().length) return true
|
||||
return false
|
||||
static supportsPackage(pack, type) {
|
||||
if (pack.getType() === type && pack.getStylesheetPaths().length)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constructor (pack) {
|
||||
super()
|
||||
this.pack = pack
|
||||
this.watch()
|
||||
constructor(pack) {
|
||||
super();
|
||||
this.pack = pack;
|
||||
this.watch();
|
||||
}
|
||||
|
||||
watch () {
|
||||
const watchedPaths = []
|
||||
watch() {
|
||||
const watchedPaths = [];
|
||||
const watchPath = stylesheet => {
|
||||
if (!watchedPaths.includes(stylesheet)) this.watchFile(stylesheet)
|
||||
watchedPaths.push(stylesheet)
|
||||
}
|
||||
if (!watchedPaths.includes(stylesheet)) this.watchFile(stylesheet);
|
||||
watchedPaths.push(stylesheet);
|
||||
};
|
||||
|
||||
const stylesheetsPath = this.pack.getStylesheetsPath()
|
||||
const stylesheetsPath = this.pack.getStylesheetsPath();
|
||||
|
||||
if (fs.isDirectorySync(stylesheetsPath)) {
|
||||
this.watchDirectory(stylesheetsPath)
|
||||
this.watchDirectory(stylesheetsPath);
|
||||
}
|
||||
|
||||
const stylesheetPaths = new Set(this.pack.getStylesheetPaths())
|
||||
const onFile = stylesheetPath => stylesheetPaths.add(stylesheetPath)
|
||||
const onFolder = () => true
|
||||
fs.traverseTreeSync(stylesheetsPath, onFile, onFolder)
|
||||
const stylesheetPaths = new Set(this.pack.getStylesheetPaths());
|
||||
const onFile = stylesheetPath => stylesheetPaths.add(stylesheetPath);
|
||||
const onFolder = () => true;
|
||||
fs.traverseTreeSync(stylesheetsPath, onFile, onFolder);
|
||||
|
||||
for (let stylesheet of stylesheetPaths) {
|
||||
watchPath(stylesheet)
|
||||
watchPath(stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
loadStylesheet (pathName) {
|
||||
if (pathName.includes('variables')) this.emitGlobalsChanged()
|
||||
this.loadAllStylesheets()
|
||||
loadStylesheet(pathName) {
|
||||
if (pathName.includes('variables')) this.emitGlobalsChanged();
|
||||
this.loadAllStylesheets();
|
||||
}
|
||||
|
||||
loadAllStylesheets () {
|
||||
console.log('Reloading package', this.pack.name)
|
||||
this.pack.reloadStylesheets()
|
||||
loadAllStylesheets() {
|
||||
console.log('Reloading package', this.pack.name);
|
||||
this.pack.reloadStylesheets();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,114 +1,114 @@
|
||||
const { CompositeDisposable } = require('atom')
|
||||
const { CompositeDisposable } = require('atom');
|
||||
|
||||
const BaseThemeWatcher = require('./base-theme-watcher')
|
||||
const PackageWatcher = require('./package-watcher')
|
||||
const BaseThemeWatcher = require('./base-theme-watcher');
|
||||
const PackageWatcher = require('./package-watcher');
|
||||
|
||||
module.exports = class UIWatcher {
|
||||
constructor () {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
this.reloadAll = this.reloadAll.bind(this)
|
||||
this.watchers = []
|
||||
this.baseTheme = this.createWatcher(new BaseThemeWatcher())
|
||||
this.watchPackages()
|
||||
constructor() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
this.reloadAll = this.reloadAll.bind(this);
|
||||
this.watchers = [];
|
||||
this.baseTheme = this.createWatcher(new BaseThemeWatcher());
|
||||
this.watchPackages();
|
||||
}
|
||||
|
||||
watchPackages () {
|
||||
this.watchedThemes = new Map()
|
||||
this.watchedPackages = new Map()
|
||||
watchPackages() {
|
||||
this.watchedThemes = new Map();
|
||||
this.watchedPackages = new Map();
|
||||
for (const theme of atom.themes.getActiveThemes()) {
|
||||
this.watchTheme(theme)
|
||||
this.watchTheme(theme);
|
||||
}
|
||||
for (const pack of atom.packages.getActivePackages()) {
|
||||
this.watchPackage(pack)
|
||||
this.watchPackage(pack);
|
||||
}
|
||||
this.watchForPackageChanges()
|
||||
this.watchForPackageChanges();
|
||||
}
|
||||
|
||||
watchForPackageChanges () {
|
||||
watchForPackageChanges() {
|
||||
this.subscriptions.add(
|
||||
atom.themes.onDidChangeActiveThemes(() => {
|
||||
// We need to destroy all theme watchers as all theme packages are destroyed
|
||||
// when a theme changes.
|
||||
for (const theme of this.watchedThemes.values()) {
|
||||
theme.destroy()
|
||||
theme.destroy();
|
||||
}
|
||||
|
||||
this.watchedThemes.clear()
|
||||
this.watchedThemes.clear();
|
||||
|
||||
// Rewatch everything!
|
||||
for (const theme of atom.themes.getActiveThemes()) {
|
||||
this.watchTheme(theme)
|
||||
this.watchTheme(theme);
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.packages.onDidActivatePackage(pack => this.watchPackage(pack))
|
||||
)
|
||||
);
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.packages.onDidDeactivatePackage(pack => {
|
||||
// This only handles packages - onDidChangeActiveThemes handles themes
|
||||
const watcher = this.watchedPackages.get(pack.name)
|
||||
if (watcher) watcher.destroy()
|
||||
this.watchedPackages.delete(pack.name)
|
||||
const watcher = this.watchedPackages.get(pack.name);
|
||||
if (watcher) watcher.destroy();
|
||||
this.watchedPackages.delete(pack.name);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
watchTheme (theme) {
|
||||
watchTheme(theme) {
|
||||
if (PackageWatcher.supportsPackage(theme, 'theme')) {
|
||||
this.watchedThemes.set(
|
||||
theme.name,
|
||||
this.createWatcher(new PackageWatcher(theme))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
watchPackage (pack) {
|
||||
watchPackage(pack) {
|
||||
if (PackageWatcher.supportsPackage(pack, 'atom')) {
|
||||
this.watchedPackages.set(
|
||||
pack.name,
|
||||
this.createWatcher(new PackageWatcher(pack))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createWatcher (watcher) {
|
||||
createWatcher(watcher) {
|
||||
watcher.onDidChangeGlobals(() => {
|
||||
console.log('Global changed, reloading all styles')
|
||||
this.reloadAll()
|
||||
})
|
||||
console.log('Global changed, reloading all styles');
|
||||
this.reloadAll();
|
||||
});
|
||||
watcher.onDidDestroy(() =>
|
||||
this.watchers.splice(this.watchers.indexOf(watcher), 1)
|
||||
)
|
||||
this.watchers.push(watcher)
|
||||
return watcher
|
||||
);
|
||||
this.watchers.push(watcher);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
reloadAll () {
|
||||
this.baseTheme.loadAllStylesheets()
|
||||
reloadAll() {
|
||||
this.baseTheme.loadAllStylesheets();
|
||||
for (const pack of atom.packages.getActivePackages()) {
|
||||
if (PackageWatcher.supportsPackage(pack, 'atom')) {
|
||||
pack.reloadStylesheets()
|
||||
pack.reloadStylesheets();
|
||||
}
|
||||
}
|
||||
|
||||
for (const theme of atom.themes.getActiveThemes()) {
|
||||
if (PackageWatcher.supportsPackage(theme, 'theme')) {
|
||||
theme.reloadStylesheets()
|
||||
theme.reloadStylesheets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.subscriptions.dispose()
|
||||
this.baseTheme.destroy()
|
||||
destroy() {
|
||||
this.subscriptions.dispose();
|
||||
this.baseTheme.destroy();
|
||||
for (const pack of this.watchedPackages.values()) {
|
||||
pack.destroy()
|
||||
pack.destroy();
|
||||
}
|
||||
for (const theme of this.watchedThemes.values()) {
|
||||
theme.destroy()
|
||||
theme.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,74 +1,74 @@
|
||||
const { CompositeDisposable, File, Directory, Emitter } = require('atom')
|
||||
const path = require('path')
|
||||
const { CompositeDisposable, File, Directory, Emitter } = require('atom');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = class Watcher {
|
||||
constructor () {
|
||||
this.destroy = this.destroy.bind(this)
|
||||
this.emitter = new Emitter()
|
||||
this.disposables = new CompositeDisposable()
|
||||
this.entities = [] // Used for specs
|
||||
constructor() {
|
||||
this.destroy = this.destroy.bind(this);
|
||||
this.emitter = new Emitter();
|
||||
this.disposables = new CompositeDisposable();
|
||||
this.entities = []; // Used for specs
|
||||
}
|
||||
|
||||
onDidDestroy (callback) {
|
||||
this.emitter.on('did-destroy', callback)
|
||||
onDidDestroy(callback) {
|
||||
this.emitter.on('did-destroy', callback);
|
||||
}
|
||||
|
||||
onDidChangeGlobals (callback) {
|
||||
this.emitter.on('did-change-globals', callback)
|
||||
onDidChangeGlobals(callback) {
|
||||
this.emitter.on('did-change-globals', callback);
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.disposables.dispose()
|
||||
this.entities = null
|
||||
this.emitter.emit('did-destroy')
|
||||
this.emitter.dispose()
|
||||
destroy() {
|
||||
this.disposables.dispose();
|
||||
this.entities = null;
|
||||
this.emitter.emit('did-destroy');
|
||||
this.emitter.dispose();
|
||||
}
|
||||
|
||||
watch () {
|
||||
watch() {
|
||||
// override me
|
||||
}
|
||||
|
||||
loadStylesheet (stylesheetPath) {
|
||||
loadStylesheet(stylesheetPath) {
|
||||
// override me
|
||||
}
|
||||
|
||||
loadAllStylesheets () {
|
||||
loadAllStylesheets() {
|
||||
// override me
|
||||
}
|
||||
|
||||
emitGlobalsChanged () {
|
||||
this.emitter.emit('did-change-globals')
|
||||
emitGlobalsChanged() {
|
||||
this.emitter.emit('did-change-globals');
|
||||
}
|
||||
|
||||
watchDirectory (directoryPath) {
|
||||
if (this.isInAsarArchive(directoryPath)) return
|
||||
const entity = new Directory(directoryPath)
|
||||
this.disposables.add(entity.onDidChange(() => this.loadAllStylesheets()))
|
||||
this.entities.push(entity)
|
||||
watchDirectory(directoryPath) {
|
||||
if (this.isInAsarArchive(directoryPath)) return;
|
||||
const entity = new Directory(directoryPath);
|
||||
this.disposables.add(entity.onDidChange(() => this.loadAllStylesheets()));
|
||||
this.entities.push(entity);
|
||||
}
|
||||
|
||||
watchGlobalFile (filePath) {
|
||||
const entity = new File(filePath)
|
||||
this.disposables.add(entity.onDidChange(() => this.emitGlobalsChanged()))
|
||||
this.entities.push(entity)
|
||||
watchGlobalFile(filePath) {
|
||||
const entity = new File(filePath);
|
||||
this.disposables.add(entity.onDidChange(() => this.emitGlobalsChanged()));
|
||||
this.entities.push(entity);
|
||||
}
|
||||
|
||||
watchFile (filePath) {
|
||||
if (this.isInAsarArchive(filePath)) return
|
||||
const reloadFn = () => this.loadStylesheet(entity.getPath())
|
||||
watchFile(filePath) {
|
||||
if (this.isInAsarArchive(filePath)) return;
|
||||
const reloadFn = () => this.loadStylesheet(entity.getPath());
|
||||
|
||||
const entity = new File(filePath)
|
||||
this.disposables.add(entity.onDidChange(reloadFn))
|
||||
this.disposables.add(entity.onDidDelete(reloadFn))
|
||||
this.disposables.add(entity.onDidRename(reloadFn))
|
||||
this.entities.push(entity)
|
||||
const entity = new File(filePath);
|
||||
this.disposables.add(entity.onDidChange(reloadFn));
|
||||
this.disposables.add(entity.onDidDelete(reloadFn));
|
||||
this.disposables.add(entity.onDidRename(reloadFn));
|
||||
this.entities.push(entity);
|
||||
}
|
||||
|
||||
isInAsarArchive (pathToCheck) {
|
||||
const { resourcePath } = atom.getLoadSettings()
|
||||
isInAsarArchive(pathToCheck) {
|
||||
const { resourcePath } = atom.getLoadSettings();
|
||||
return (
|
||||
pathToCheck.startsWith(`${resourcePath}${path.sep}`) &&
|
||||
path.extname(resourcePath) === '.asar'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +1,26 @@
|
||||
/** @babel */
|
||||
|
||||
export async function conditionPromise (
|
||||
export async function conditionPromise(
|
||||
condition,
|
||||
description = 'anonymous condition'
|
||||
) {
|
||||
const startTime = Date.now()
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
await timeoutPromise(100)
|
||||
await timeoutPromise(100);
|
||||
|
||||
if (await condition()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - startTime > 5000) {
|
||||
throw new Error('Timed out waiting on ' + description)
|
||||
throw new Error('Timed out waiting on ' + description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function timeoutPromise (timeout) {
|
||||
return new Promise(function (resolve) {
|
||||
global.setTimeout(resolve, timeout)
|
||||
})
|
||||
export function timeoutPromise(timeout) {
|
||||
return new Promise(function(resolve) {
|
||||
global.setTimeout(resolve, timeout);
|
||||
});
|
||||
}
|
||||
|
@ -1,128 +1,128 @@
|
||||
describe('Dev Live Reload', () => {
|
||||
describe('package activation', () => {
|
||||
let [pack, mainModule] = []
|
||||
let [pack, mainModule] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
pack = atom.packages.loadPackage('dev-live-reload')
|
||||
pack.requireMainModule()
|
||||
mainModule = pack.mainModule
|
||||
spyOn(mainModule, 'startWatching')
|
||||
})
|
||||
pack = atom.packages.loadPackage('dev-live-reload');
|
||||
pack.requireMainModule();
|
||||
mainModule = pack.mainModule;
|
||||
spyOn(mainModule, 'startWatching');
|
||||
});
|
||||
|
||||
describe('when the window is not in dev mode', () => {
|
||||
beforeEach(() => spyOn(atom, 'inDevMode').andReturn(false))
|
||||
beforeEach(() => spyOn(atom, 'inDevMode').andReturn(false));
|
||||
|
||||
it('does not watch files', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the window is in spec mode', () => {
|
||||
beforeEach(() => spyOn(atom, 'inSpecMode').andReturn(true))
|
||||
beforeEach(() => spyOn(atom, 'inSpecMode').andReturn(true));
|
||||
|
||||
it('does not watch files', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the window is in dev mode', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(atom, 'inDevMode').andReturn(true)
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
})
|
||||
spyOn(atom, 'inDevMode').andReturn(true);
|
||||
spyOn(atom, 'inSpecMode').andReturn(false);
|
||||
});
|
||||
|
||||
it('watches files', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
expect(mainModule.startWatching).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(mainModule.startWatching).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the window is in both dev mode and spec mode', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(atom, 'inDevMode').andReturn(true)
|
||||
spyOn(atom, 'inSpecMode').andReturn(true)
|
||||
})
|
||||
spyOn(atom, 'inDevMode').andReturn(true);
|
||||
spyOn(atom, 'inSpecMode').andReturn(true);
|
||||
});
|
||||
|
||||
it('does not watch files', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the package is activated before initial packages have been activated', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(atom, 'inDevMode').andReturn(true)
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
})
|
||||
spyOn(atom, 'inDevMode').andReturn(true);
|
||||
spyOn(atom, 'inSpecMode').andReturn(false);
|
||||
});
|
||||
|
||||
it('waits until all initial packages have been activated before watching files', async () => {
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled()
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled();
|
||||
|
||||
atom.packages.emitter.emit('did-activate-initial-packages')
|
||||
expect(mainModule.startWatching).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
atom.packages.emitter.emit('did-activate-initial-packages');
|
||||
expect(mainModule.startWatching).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('package deactivation', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(atom, 'inDevMode').andReturn(true)
|
||||
spyOn(atom, 'inSpecMode').andReturn(false)
|
||||
})
|
||||
spyOn(atom, 'inDevMode').andReturn(true);
|
||||
spyOn(atom, 'inSpecMode').andReturn(false);
|
||||
});
|
||||
|
||||
it('stops watching all files', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
const { mainModule } = await atom.packages.activatePackage(
|
||||
'dev-live-reload'
|
||||
)
|
||||
expect(mainModule.uiWatcher).not.toBeNull()
|
||||
);
|
||||
expect(mainModule.uiWatcher).not.toBeNull();
|
||||
|
||||
spyOn(mainModule.uiWatcher, 'destroy')
|
||||
spyOn(mainModule.uiWatcher, 'destroy');
|
||||
|
||||
await atom.packages.deactivatePackage('dev-live-reload')
|
||||
expect(mainModule.uiWatcher.destroy).toHaveBeenCalled()
|
||||
})
|
||||
await atom.packages.deactivatePackage('dev-live-reload');
|
||||
expect(mainModule.uiWatcher.destroy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('unsubscribes from the onDidActivateInitialPackages subscription if it is disabled before all initial packages are activated', async () => {
|
||||
const { mainModule } = await atom.packages.activatePackage(
|
||||
'dev-live-reload'
|
||||
)
|
||||
expect(mainModule.activatedDisposable.disposed).toBe(false)
|
||||
);
|
||||
expect(mainModule.activatedDisposable.disposed).toBe(false);
|
||||
|
||||
await atom.packages.deactivatePackage('dev-live-reload')
|
||||
expect(mainModule.activatedDisposable.disposed).toBe(true)
|
||||
await atom.packages.deactivatePackage('dev-live-reload');
|
||||
expect(mainModule.activatedDisposable.disposed).toBe(true);
|
||||
|
||||
spyOn(mainModule, 'startWatching')
|
||||
atom.packages.emitter.emit('did-activate-initial-packages')
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled()
|
||||
})
|
||||
spyOn(mainModule, 'startWatching');
|
||||
atom.packages.emitter.emit('did-activate-initial-packages');
|
||||
expect(mainModule.startWatching).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('removes its commands', async () => {
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
|
||||
await atom.packages.activatePackage('dev-live-reload')
|
||||
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true);
|
||||
await atom.packages.activatePackage('dev-live-reload');
|
||||
expect(
|
||||
atom.commands
|
||||
.findCommands({ target: atom.views.getView(atom.workspace) })
|
||||
.filter(command => command.name.startsWith('dev-live-reload')).length
|
||||
).toBeGreaterThan(0)
|
||||
).toBeGreaterThan(0);
|
||||
|
||||
await atom.packages.deactivatePackage('dev-live-reload')
|
||||
await atom.packages.deactivatePackage('dev-live-reload');
|
||||
expect(
|
||||
atom.commands
|
||||
.findCommands({ target: atom.views.getView(atom.workspace) })
|
||||
.filter(command => command.name.startsWith('dev-live-reload')).length
|
||||
).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,260 +1,262 @@
|
||||
const path = require('path')
|
||||
const path = require('path');
|
||||
|
||||
const UIWatcher = require('../lib/ui-watcher')
|
||||
const UIWatcher = require('../lib/ui-watcher');
|
||||
|
||||
const { conditionPromise } = require('./async-spec-helpers')
|
||||
const { conditionPromise } = require('./async-spec-helpers');
|
||||
|
||||
describe('UIWatcher', () => {
|
||||
let uiWatcher = null
|
||||
let uiWatcher = null;
|
||||
|
||||
beforeEach(() =>
|
||||
atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures'))
|
||||
)
|
||||
);
|
||||
|
||||
afterEach(() => uiWatcher && uiWatcher.destroy())
|
||||
afterEach(() => uiWatcher && uiWatcher.destroy());
|
||||
|
||||
describe("when a base theme's file changes", () => {
|
||||
beforeEach(() => {
|
||||
spyOn(atom.themes, 'resolveStylesheet').andReturn(
|
||||
path.join(__dirname, 'fixtures', 'static', 'atom.less')
|
||||
)
|
||||
uiWatcher = new UIWatcher()
|
||||
})
|
||||
);
|
||||
uiWatcher = new UIWatcher();
|
||||
});
|
||||
|
||||
it('reloads all the base styles', () => {
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets')
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets');
|
||||
|
||||
expect(uiWatcher.baseTheme.entities[0].getPath()).toContain(
|
||||
`${path.sep}static${path.sep}`
|
||||
)
|
||||
);
|
||||
|
||||
uiWatcher.baseTheme.entities[0].emitter.emit('did-change')
|
||||
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
uiWatcher.baseTheme.entities[0].emitter.emit('did-change');
|
||||
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("watches all the style sheets in the theme's styles folder", async () => {
|
||||
const packagePath = path.join(
|
||||
__dirname,
|
||||
'fixtures',
|
||||
'package-with-styles-folder'
|
||||
)
|
||||
);
|
||||
|
||||
await atom.packages.activatePackage(packagePath)
|
||||
uiWatcher = new UIWatcher()
|
||||
await atom.packages.activatePackage(packagePath);
|
||||
uiWatcher = new UIWatcher();
|
||||
|
||||
const lastWatcher = uiWatcher.watchers[uiWatcher.watchers.length - 1]
|
||||
const lastWatcher = uiWatcher.watchers[uiWatcher.watchers.length - 1];
|
||||
|
||||
expect(lastWatcher.entities.length).toBe(4)
|
||||
expect(lastWatcher.entities.length).toBe(4);
|
||||
expect(lastWatcher.entities[0].getPath()).toBe(
|
||||
path.join(packagePath, 'styles')
|
||||
)
|
||||
);
|
||||
expect(lastWatcher.entities[1].getPath()).toBe(
|
||||
path.join(packagePath, 'styles', '3.css')
|
||||
)
|
||||
);
|
||||
expect(lastWatcher.entities[2].getPath()).toBe(
|
||||
path.join(packagePath, 'styles', 'sub', '1.css')
|
||||
)
|
||||
);
|
||||
expect(lastWatcher.entities[3].getPath()).toBe(
|
||||
path.join(packagePath, 'styles', 'sub', '2.less')
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('when a package stylesheet file changes', async () => {
|
||||
beforeEach(async () => {
|
||||
await atom.packages.activatePackage(
|
||||
path.join(__dirname, 'fixtures', 'package-with-styles-manifest')
|
||||
)
|
||||
uiWatcher = new UIWatcher()
|
||||
})
|
||||
);
|
||||
uiWatcher = new UIWatcher();
|
||||
});
|
||||
|
||||
it('reloads all package styles', () => {
|
||||
const pack = atom.packages.getActivePackages()[0]
|
||||
spyOn(pack, 'reloadStylesheets')
|
||||
const pack = atom.packages.getActivePackages()[0];
|
||||
spyOn(pack, 'reloadStylesheets');
|
||||
|
||||
uiWatcher.watchers[uiWatcher.watchers.length - 1].entities[1].emitter.emit('did-change')
|
||||
uiWatcher.watchers[
|
||||
uiWatcher.watchers.length - 1
|
||||
].entities[1].emitter.emit('did-change');
|
||||
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a package does not have a stylesheet', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.packages.activatePackage('package-with-index')
|
||||
uiWatcher = new UIWatcher()
|
||||
})
|
||||
await atom.packages.activatePackage('package-with-index');
|
||||
uiWatcher = new UIWatcher();
|
||||
});
|
||||
|
||||
it('does not create a PackageWatcher', () => {
|
||||
expect(uiWatcher.watchedPackages['package-with-index']).toBeUndefined()
|
||||
})
|
||||
})
|
||||
expect(uiWatcher.watchedPackages['package-with-index']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a package global file changes', () => {
|
||||
beforeEach(async () => {
|
||||
atom.config.set('core.themes', [
|
||||
'theme-with-ui-variables',
|
||||
'theme-with-multiple-imported-files'
|
||||
])
|
||||
]);
|
||||
|
||||
await atom.themes.activateThemes()
|
||||
uiWatcher = new UIWatcher()
|
||||
})
|
||||
await atom.themes.activateThemes();
|
||||
uiWatcher = new UIWatcher();
|
||||
});
|
||||
|
||||
afterEach(() => atom.themes.deactivateThemes())
|
||||
afterEach(() => atom.themes.deactivateThemes());
|
||||
|
||||
it('reloads every package when the variables file changes', () => {
|
||||
let varEntity
|
||||
let varEntity;
|
||||
for (const theme of atom.themes.getActiveThemes()) {
|
||||
spyOn(theme, 'reloadStylesheets')
|
||||
spyOn(theme, 'reloadStylesheets');
|
||||
}
|
||||
|
||||
for (const entity of uiWatcher.watchedThemes.get(
|
||||
'theme-with-multiple-imported-files'
|
||||
).entities) {
|
||||
if (entity.getPath().indexOf('variables') > -1) varEntity = entity
|
||||
if (entity.getPath().indexOf('variables') > -1) varEntity = entity;
|
||||
}
|
||||
varEntity.emitter.emit('did-change')
|
||||
varEntity.emitter.emit('did-change');
|
||||
|
||||
for (const theme of atom.themes.getActiveThemes()) {
|
||||
expect(theme.reloadStylesheets).toHaveBeenCalled()
|
||||
expect(theme.reloadStylesheets).toHaveBeenCalled();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('watcher lifecycle', () => {
|
||||
it('starts watching a package if it is activated after initial startup', async () => {
|
||||
uiWatcher = new UIWatcher()
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0)
|
||||
uiWatcher = new UIWatcher();
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0);
|
||||
|
||||
await atom.packages.activatePackage(
|
||||
path.join(__dirname, 'fixtures', 'package-with-styles-folder')
|
||||
)
|
||||
);
|
||||
expect(
|
||||
uiWatcher.watchedPackages.get('package-with-styles-folder')
|
||||
).not.toBeUndefined()
|
||||
})
|
||||
).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('unwatches a package after it is deactivated', async () => {
|
||||
await atom.packages.activatePackage(
|
||||
path.join(__dirname, 'fixtures', 'package-with-styles-folder')
|
||||
)
|
||||
uiWatcher = new UIWatcher()
|
||||
);
|
||||
uiWatcher = new UIWatcher();
|
||||
const watcher = uiWatcher.watchedPackages.get(
|
||||
'package-with-styles-folder'
|
||||
)
|
||||
expect(watcher).not.toBeUndefined()
|
||||
);
|
||||
expect(watcher).not.toBeUndefined();
|
||||
|
||||
const watcherDestructionSpy = jasmine.createSpy('watcher-on-did-destroy')
|
||||
watcher.onDidDestroy(watcherDestructionSpy)
|
||||
const watcherDestructionSpy = jasmine.createSpy('watcher-on-did-destroy');
|
||||
watcher.onDidDestroy(watcherDestructionSpy);
|
||||
|
||||
await atom.packages.deactivatePackage('package-with-styles-folder')
|
||||
await atom.packages.deactivatePackage('package-with-styles-folder');
|
||||
expect(
|
||||
uiWatcher.watchedPackages.get('package-with-styles-folder')
|
||||
).toBeUndefined()
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0)
|
||||
expect(watcherDestructionSpy).toHaveBeenCalled()
|
||||
})
|
||||
).toBeUndefined();
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0);
|
||||
expect(watcherDestructionSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not watch activated packages after the UI watcher has been destroyed', async () => {
|
||||
uiWatcher = new UIWatcher()
|
||||
uiWatcher.destroy()
|
||||
uiWatcher = new UIWatcher();
|
||||
uiWatcher.destroy();
|
||||
|
||||
await atom.packages.activatePackage(
|
||||
path.join(__dirname, 'fixtures', 'package-with-styles-folder')
|
||||
)
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0)
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(uiWatcher.watchedPackages.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('minimal theme packages', () => {
|
||||
let pack = null
|
||||
let pack = null;
|
||||
beforeEach(async () => {
|
||||
atom.config.set('core.themes', [
|
||||
'theme-with-syntax-variables',
|
||||
'theme-with-index-less'
|
||||
])
|
||||
await atom.themes.activateThemes()
|
||||
uiWatcher = new UIWatcher()
|
||||
pack = atom.themes.getActiveThemes()[0]
|
||||
})
|
||||
]);
|
||||
await atom.themes.activateThemes();
|
||||
uiWatcher = new UIWatcher();
|
||||
pack = atom.themes.getActiveThemes()[0];
|
||||
});
|
||||
|
||||
afterEach(() => atom.themes.deactivateThemes())
|
||||
afterEach(() => atom.themes.deactivateThemes());
|
||||
|
||||
it('watches themes without a styles directory', () => {
|
||||
spyOn(pack, 'reloadStylesheets')
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets')
|
||||
spyOn(pack, 'reloadStylesheets');
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets');
|
||||
|
||||
const watcher = uiWatcher.watchedThemes.get('theme-with-index-less')
|
||||
const watcher = uiWatcher.watchedThemes.get('theme-with-index-less');
|
||||
|
||||
expect(watcher.entities.length).toBe(1)
|
||||
expect(watcher.entities.length).toBe(1);
|
||||
|
||||
watcher.entities[0].emitter.emit('did-change')
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled()
|
||||
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
watcher.entities[0].emitter.emit('did-change');
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled();
|
||||
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('theme packages', () => {
|
||||
let pack = null
|
||||
let pack = null;
|
||||
beforeEach(async () => {
|
||||
atom.config.set('core.themes', [
|
||||
'theme-with-syntax-variables',
|
||||
'theme-with-multiple-imported-files'
|
||||
])
|
||||
]);
|
||||
|
||||
await atom.themes.activateThemes()
|
||||
uiWatcher = new UIWatcher()
|
||||
pack = atom.themes.getActiveThemes()[0]
|
||||
})
|
||||
await atom.themes.activateThemes();
|
||||
uiWatcher = new UIWatcher();
|
||||
pack = atom.themes.getActiveThemes()[0];
|
||||
});
|
||||
|
||||
afterEach(() => atom.themes.deactivateThemes())
|
||||
afterEach(() => atom.themes.deactivateThemes());
|
||||
|
||||
it('reloads the theme when anything within the theme changes', () => {
|
||||
spyOn(pack, 'reloadStylesheets')
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets')
|
||||
spyOn(pack, 'reloadStylesheets');
|
||||
spyOn(atom.themes, 'reloadBaseStylesheets');
|
||||
|
||||
const watcher = uiWatcher.watchedThemes.get(
|
||||
'theme-with-multiple-imported-files'
|
||||
)
|
||||
);
|
||||
|
||||
expect(watcher.entities.length).toBe(6)
|
||||
expect(watcher.entities.length).toBe(6);
|
||||
|
||||
watcher.entities[2].emitter.emit('did-change')
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled()
|
||||
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled()
|
||||
watcher.entities[2].emitter.emit('did-change');
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled();
|
||||
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled();
|
||||
|
||||
watcher.entities[watcher.entities.length - 1].emitter.emit('did-change')
|
||||
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled()
|
||||
})
|
||||
watcher.entities[watcher.entities.length - 1].emitter.emit('did-change');
|
||||
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('unwatches when a theme is deactivated', async () => {
|
||||
jasmine.useRealClock()
|
||||
jasmine.useRealClock();
|
||||
|
||||
atom.config.set('core.themes', [])
|
||||
atom.config.set('core.themes', []);
|
||||
await conditionPromise(
|
||||
() => !uiWatcher.watchedThemes['theme-with-multiple-imported-files']
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('watches a new theme when it is deactivated', async () => {
|
||||
jasmine.useRealClock()
|
||||
jasmine.useRealClock();
|
||||
|
||||
atom.config.set('core.themes', [
|
||||
'theme-with-syntax-variables',
|
||||
'theme-with-package-file'
|
||||
])
|
||||
]);
|
||||
await conditionPromise(() =>
|
||||
uiWatcher.watchedThemes.get('theme-with-package-file')
|
||||
)
|
||||
);
|
||||
|
||||
pack = atom.themes.getActiveThemes()[0]
|
||||
spyOn(pack, 'reloadStylesheets')
|
||||
pack = atom.themes.getActiveThemes()[0];
|
||||
spyOn(pack, 'reloadStylesheets');
|
||||
|
||||
expect(pack.name).toBe('theme-with-package-file')
|
||||
expect(pack.name).toBe('theme-with-package-file');
|
||||
|
||||
const watcher = uiWatcher.watchedThemes.get('theme-with-package-file')
|
||||
watcher.entities[2].emitter.emit('did-change')
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
const watcher = uiWatcher.watchedThemes.get('theme-with-package-file');
|
||||
watcher.entities[2].emitter.emit('did-change');
|
||||
expect(pack.reloadStylesheets).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,57 +1,57 @@
|
||||
/** @babel */
|
||||
|
||||
import { CompositeDisposable } from 'atom'
|
||||
import { CompositeDisposable } from 'atom';
|
||||
|
||||
let reporter
|
||||
let reporter;
|
||||
|
||||
function getReporter () {
|
||||
function getReporter() {
|
||||
if (!reporter) {
|
||||
const Reporter = require('./reporter')
|
||||
reporter = new Reporter()
|
||||
const Reporter = require('./reporter');
|
||||
reporter = new Reporter();
|
||||
}
|
||||
return reporter
|
||||
return reporter;
|
||||
}
|
||||
|
||||
export default {
|
||||
activate () {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
activate() {
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
|
||||
if (!atom.config.get('exception-reporting.userId')) {
|
||||
atom.config.set('exception-reporting.userId', require('node-uuid').v4())
|
||||
atom.config.set('exception-reporting.userId', require('node-uuid').v4());
|
||||
}
|
||||
|
||||
this.subscriptions.add(
|
||||
atom.onDidThrowError(({ message, url, line, column, originalError }) => {
|
||||
try {
|
||||
getReporter().reportUncaughtException(originalError)
|
||||
getReporter().reportUncaughtException(originalError);
|
||||
} catch (secondaryException) {
|
||||
try {
|
||||
console.error(
|
||||
'Error reporting uncaught exception',
|
||||
secondaryException
|
||||
)
|
||||
getReporter().reportUncaughtException(secondaryException)
|
||||
);
|
||||
getReporter().reportUncaughtException(secondaryException);
|
||||
} catch (error) {}
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
if (atom.onDidFailAssertion != null) {
|
||||
this.subscriptions.add(
|
||||
atom.onDidFailAssertion(error => {
|
||||
try {
|
||||
getReporter().reportFailedAssertion(error)
|
||||
getReporter().reportFailedAssertion(error);
|
||||
} catch (secondaryException) {
|
||||
try {
|
||||
console.error(
|
||||
'Error reporting assertion failure',
|
||||
secondaryException
|
||||
)
|
||||
getReporter().reportUncaughtException(secondaryException)
|
||||
);
|
||||
getReporter().reportUncaughtException(secondaryException);
|
||||
} catch (error) {}
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,31 +1,31 @@
|
||||
/** @babel */
|
||||
|
||||
import os from 'os'
|
||||
import stackTrace from 'stack-trace'
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
import os from 'os';
|
||||
import stackTrace from 'stack-trace';
|
||||
import fs from 'fs-plus';
|
||||
import path from 'path';
|
||||
|
||||
const API_KEY = '7ddca14cb60cbd1cd12d1b252473b076'
|
||||
const LIB_VERSION = require('../package.json')['version']
|
||||
const StackTraceCache = new WeakMap()
|
||||
const API_KEY = '7ddca14cb60cbd1cd12d1b252473b076';
|
||||
const LIB_VERSION = require('../package.json')['version'];
|
||||
const StackTraceCache = new WeakMap();
|
||||
|
||||
export default class Reporter {
|
||||
constructor (params = {}) {
|
||||
this.request = params.request || window.fetch
|
||||
constructor(params = {}) {
|
||||
this.request = params.request || window.fetch;
|
||||
this.alwaysReport = params.hasOwnProperty('alwaysReport')
|
||||
? params.alwaysReport
|
||||
: false
|
||||
: false;
|
||||
this.reportPreviousErrors = params.hasOwnProperty('reportPreviousErrors')
|
||||
? params.reportPreviousErrors
|
||||
: true
|
||||
: true;
|
||||
this.resourcePath = this.normalizePath(
|
||||
params.resourcePath || process.resourcesPath
|
||||
)
|
||||
this.reportedErrors = []
|
||||
this.reportedAssertionFailures = []
|
||||
);
|
||||
this.reportedErrors = [];
|
||||
this.reportedAssertionFailures = [];
|
||||
}
|
||||
|
||||
buildNotificationJSON (error, params) {
|
||||
buildNotificationJSON(error, params) {
|
||||
return {
|
||||
apiKey: API_KEY,
|
||||
notifier: {
|
||||
@ -51,18 +51,18 @@ export default class Reporter {
|
||||
metaData: error.metadata
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
buildExceptionJSON (error, projectRoot) {
|
||||
buildExceptionJSON(error, projectRoot) {
|
||||
return {
|
||||
errorClass: error.constructor.name,
|
||||
message: error.message,
|
||||
stacktrace: this.buildStackTraceJSON(error, projectRoot)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
buildStackTraceJSON (error, projectRoot) {
|
||||
buildStackTraceJSON(error, projectRoot) {
|
||||
return this.parseStackTrace(error).map(callSite => {
|
||||
return {
|
||||
file: this.scrubPath(callSite.getFileName()),
|
||||
@ -71,110 +71,110 @@ export default class Reporter {
|
||||
lineNumber: callSite.getLineNumber(),
|
||||
columnNumber: callSite.getColumnNumber(),
|
||||
inProject: !/node_modules/.test(callSite.getFileName())
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
normalizePath (pathToNormalize) {
|
||||
normalizePath(pathToNormalize) {
|
||||
return pathToNormalize
|
||||
.replace('file:///', '') // Sometimes it's a uri
|
||||
.replace(/\\/g, '/') // Unify path separators across Win/macOS/Linux
|
||||
.replace(/\\/g, '/'); // Unify path separators across Win/macOS/Linux
|
||||
}
|
||||
|
||||
scrubPath (pathToScrub) {
|
||||
const absolutePath = this.normalizePath(pathToScrub)
|
||||
scrubPath(pathToScrub) {
|
||||
const absolutePath = this.normalizePath(pathToScrub);
|
||||
|
||||
if (this.isBundledFile(absolutePath)) {
|
||||
return this.normalizePath(path.relative(this.resourcePath, absolutePath))
|
||||
return this.normalizePath(path.relative(this.resourcePath, absolutePath));
|
||||
} else {
|
||||
return absolutePath
|
||||
.replace(this.normalizePath(fs.getHomeDirectory()), '~') // Remove users home dir
|
||||
.replace(/.*(\/packages\/.*)/, '$1') // Remove everything before app.asar or packages
|
||||
.replace(/.*(\/packages\/.*)/, '$1'); // Remove everything before app.asar or packages
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultNotificationParams () {
|
||||
getDefaultNotificationParams() {
|
||||
return {
|
||||
userId: atom.config.get('exception-reporting.userId'),
|
||||
appVersion: atom.getVersion(),
|
||||
releaseStage: this.getReleaseChannel(atom.getVersion()),
|
||||
projectRoot: atom.getLoadSettings().resourcePath,
|
||||
osVersion: `${os.platform()}-${os.arch()}-${os.release()}`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getReleaseChannel (version) {
|
||||
getReleaseChannel(version) {
|
||||
return version.indexOf('beta') > -1
|
||||
? 'beta'
|
||||
: version.indexOf('dev') > -1
|
||||
? 'dev'
|
||||
: 'stable'
|
||||
: 'stable';
|
||||
}
|
||||
|
||||
performRequest (json) {
|
||||
performRequest(json) {
|
||||
this.request.call(null, 'https://notify.bugsnag.com', {
|
||||
method: 'POST',
|
||||
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||
body: JSON.stringify(json)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
shouldReport (error) {
|
||||
if (this.alwaysReport) return true // Used in specs
|
||||
if (atom.config.get('core.telemetryConsent') !== 'limited') return false
|
||||
if (atom.inDevMode()) return false
|
||||
shouldReport(error) {
|
||||
if (this.alwaysReport) return true; // Used in specs
|
||||
if (atom.config.get('core.telemetryConsent') !== 'limited') return false;
|
||||
if (atom.inDevMode()) return false;
|
||||
|
||||
const topFrame = this.parseStackTrace(error)[0]
|
||||
const fileName = topFrame ? topFrame.getFileName() : null
|
||||
const topFrame = this.parseStackTrace(error)[0];
|
||||
const fileName = topFrame ? topFrame.getFileName() : null;
|
||||
return (
|
||||
fileName &&
|
||||
(this.isBundledFile(fileName) || this.isTeletypeFile(fileName))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
parseStackTrace (error) {
|
||||
let callSites = StackTraceCache.get(error)
|
||||
parseStackTrace(error) {
|
||||
let callSites = StackTraceCache.get(error);
|
||||
if (callSites) {
|
||||
return callSites
|
||||
return callSites;
|
||||
} else {
|
||||
callSites = stackTrace.parse(error)
|
||||
StackTraceCache.set(error, callSites)
|
||||
return callSites
|
||||
callSites = stackTrace.parse(error);
|
||||
StackTraceCache.set(error, callSites);
|
||||
return callSites;
|
||||
}
|
||||
}
|
||||
|
||||
requestPrivateMetadataConsent (error, message, reportFn) {
|
||||
let notification, dismissSubscription
|
||||
requestPrivateMetadataConsent(error, message, reportFn) {
|
||||
let notification, dismissSubscription;
|
||||
|
||||
function reportWithoutPrivateMetadata () {
|
||||
function reportWithoutPrivateMetadata() {
|
||||
if (dismissSubscription) {
|
||||
dismissSubscription.dispose()
|
||||
dismissSubscription.dispose();
|
||||
}
|
||||
delete error.privateMetadata
|
||||
delete error.privateMetadataDescription
|
||||
reportFn(error)
|
||||
delete error.privateMetadata;
|
||||
delete error.privateMetadataDescription;
|
||||
reportFn(error);
|
||||
if (notification) {
|
||||
notification.dismiss()
|
||||
notification.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
function reportWithPrivateMetadata () {
|
||||
function reportWithPrivateMetadata() {
|
||||
if (error.metadata == null) {
|
||||
error.metadata = {}
|
||||
error.metadata = {};
|
||||
}
|
||||
for (let key in error.privateMetadata) {
|
||||
let value = error.privateMetadata[key]
|
||||
error.metadata[key] = value
|
||||
let value = error.privateMetadata[key];
|
||||
error.metadata[key] = value;
|
||||
}
|
||||
reportWithoutPrivateMetadata()
|
||||
reportWithoutPrivateMetadata();
|
||||
}
|
||||
|
||||
const name = error.privateMetadataRequestName
|
||||
const name = error.privateMetadataRequestName;
|
||||
if (name != null) {
|
||||
if (localStorage.getItem(`private-metadata-request:${name}`)) {
|
||||
return reportWithoutPrivateMetadata(error)
|
||||
return reportWithoutPrivateMetadata(error);
|
||||
} else {
|
||||
localStorage.setItem(`private-metadata-request:${name}`, true)
|
||||
localStorage.setItem(`private-metadata-request:${name}`, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,51 +193,51 @@ export default class Reporter {
|
||||
onDidClick: reportWithPrivateMetadata
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
dismissSubscription = notification.onDidDismiss(
|
||||
reportWithoutPrivateMetadata
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addPackageMetadata (error) {
|
||||
let activePackages = atom.packages.getActivePackages()
|
||||
const availablePackagePaths = atom.packages.getPackageDirPaths()
|
||||
addPackageMetadata(error) {
|
||||
let activePackages = atom.packages.getActivePackages();
|
||||
const availablePackagePaths = atom.packages.getPackageDirPaths();
|
||||
if (activePackages.length > 0) {
|
||||
let userPackages = {}
|
||||
let bundledPackages = {}
|
||||
let userPackages = {};
|
||||
let bundledPackages = {};
|
||||
for (let pack of atom.packages.getActivePackages()) {
|
||||
if (availablePackagePaths.includes(path.dirname(pack.path))) {
|
||||
userPackages[pack.name] = pack.metadata.version
|
||||
userPackages[pack.name] = pack.metadata.version;
|
||||
} else {
|
||||
bundledPackages[pack.name] = pack.metadata.version
|
||||
bundledPackages[pack.name] = pack.metadata.version;
|
||||
}
|
||||
}
|
||||
|
||||
if (error.metadata == null) {
|
||||
error.metadata = {}
|
||||
error.metadata = {};
|
||||
}
|
||||
error.metadata.bundledPackages = bundledPackages
|
||||
error.metadata.userPackages = userPackages
|
||||
error.metadata.bundledPackages = bundledPackages;
|
||||
error.metadata.userPackages = userPackages;
|
||||
}
|
||||
}
|
||||
|
||||
addPreviousErrorsMetadata (error) {
|
||||
if (!this.reportPreviousErrors) return
|
||||
if (!error.metadata) error.metadata = {}
|
||||
addPreviousErrorsMetadata(error) {
|
||||
if (!this.reportPreviousErrors) return;
|
||||
if (!error.metadata) error.metadata = {};
|
||||
error.metadata.previousErrors = this.reportedErrors.map(
|
||||
error => error.message
|
||||
)
|
||||
);
|
||||
error.metadata.previousAssertionFailures = this.reportedAssertionFailures.map(
|
||||
error => error.message
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
reportUncaughtException (error) {
|
||||
if (!this.shouldReport(error)) return
|
||||
reportUncaughtException(error) {
|
||||
if (!this.shouldReport(error)) return;
|
||||
|
||||
this.addPackageMetadata(error)
|
||||
this.addPreviousErrorsMetadata(error)
|
||||
this.addPackageMetadata(error);
|
||||
this.addPreviousErrorsMetadata(error);
|
||||
|
||||
if (
|
||||
error.privateMetadata != null &&
|
||||
@ -247,21 +247,21 @@ export default class Reporter {
|
||||
error,
|
||||
'The Atom team would like to collect the following information to resolve this error:',
|
||||
error => this.reportUncaughtException(error)
|
||||
)
|
||||
return
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let params = this.getDefaultNotificationParams()
|
||||
params.severity = 'error'
|
||||
this.performRequest(this.buildNotificationJSON(error, params))
|
||||
this.reportedErrors.push(error)
|
||||
let params = this.getDefaultNotificationParams();
|
||||
params.severity = 'error';
|
||||
this.performRequest(this.buildNotificationJSON(error, params));
|
||||
this.reportedErrors.push(error);
|
||||
}
|
||||
|
||||
reportFailedAssertion (error) {
|
||||
if (!this.shouldReport(error)) return
|
||||
reportFailedAssertion(error) {
|
||||
if (!this.shouldReport(error)) return;
|
||||
|
||||
this.addPackageMetadata(error)
|
||||
this.addPreviousErrorsMetadata(error)
|
||||
this.addPackageMetadata(error);
|
||||
this.addPreviousErrorsMetadata(error);
|
||||
|
||||
if (
|
||||
error.privateMetadata != null &&
|
||||
@ -271,32 +271,32 @@ export default class Reporter {
|
||||
error,
|
||||
'The Atom team would like to collect some information to resolve an unexpected condition:',
|
||||
error => this.reportFailedAssertion(error)
|
||||
)
|
||||
return
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let params = this.getDefaultNotificationParams()
|
||||
params.severity = 'warning'
|
||||
this.performRequest(this.buildNotificationJSON(error, params))
|
||||
this.reportedAssertionFailures.push(error)
|
||||
let params = this.getDefaultNotificationParams();
|
||||
params.severity = 'warning';
|
||||
this.performRequest(this.buildNotificationJSON(error, params));
|
||||
this.reportedAssertionFailures.push(error);
|
||||
}
|
||||
|
||||
// Used in specs
|
||||
setRequestFunction (requestFunction) {
|
||||
this.request = requestFunction
|
||||
setRequestFunction(requestFunction) {
|
||||
this.request = requestFunction;
|
||||
}
|
||||
|
||||
isBundledFile (fileName) {
|
||||
return this.normalizePath(fileName).indexOf(this.resourcePath) === 0
|
||||
isBundledFile(fileName) {
|
||||
return this.normalizePath(fileName).indexOf(this.resourcePath) === 0;
|
||||
}
|
||||
|
||||
isTeletypeFile (fileName) {
|
||||
const teletypePath = atom.packages.resolvePackagePath('teletype')
|
||||
isTeletypeFile(fileName) {
|
||||
const teletypePath = atom.packages.resolvePackagePath('teletype');
|
||||
return (
|
||||
teletypePath && this.normalizePath(fileName).indexOf(teletypePath) === 0
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reporter.API_KEY = API_KEY
|
||||
Reporter.LIB_VERSION = LIB_VERSION
|
||||
Reporter.API_KEY = API_KEY;
|
||||
Reporter.LIB_VERSION = LIB_VERSION;
|
||||
|
@ -1,76 +1,76 @@
|
||||
const Reporter = require('../lib/reporter')
|
||||
const semver = require('semver')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
let osVersion = `${os.platform()}-${os.arch()}-${os.release()}`
|
||||
const Reporter = require('../lib/reporter');
|
||||
const semver = require('semver');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const fs = require('fs-plus');
|
||||
let osVersion = `${os.platform()}-${os.arch()}-${os.release()}`;
|
||||
|
||||
let getReleaseChannel = version => {
|
||||
return version.indexOf('beta') > -1
|
||||
? 'beta'
|
||||
: version.indexOf('dev') > -1
|
||||
? 'dev'
|
||||
: 'stable'
|
||||
}
|
||||
: 'stable';
|
||||
};
|
||||
|
||||
describe('Reporter', () => {
|
||||
let reporter,
|
||||
requests,
|
||||
initialStackTraceLimit,
|
||||
initialFsGetHomeDirectory,
|
||||
mockActivePackages
|
||||
mockActivePackages;
|
||||
|
||||
beforeEach(() => {
|
||||
reporter = new Reporter({
|
||||
request: (url, options) => requests.push(Object.assign({ url }, options)),
|
||||
alwaysReport: true,
|
||||
reportPreviousErrors: false
|
||||
})
|
||||
requests = []
|
||||
mockActivePackages = []
|
||||
});
|
||||
requests = [];
|
||||
mockActivePackages = [];
|
||||
spyOn(atom.packages, 'getActivePackages').andCallFake(
|
||||
() => mockActivePackages
|
||||
)
|
||||
);
|
||||
|
||||
initialStackTraceLimit = Error.stackTraceLimit
|
||||
Error.stackTraceLimit = 1
|
||||
initialStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 1;
|
||||
|
||||
initialFsGetHomeDirectory = fs.getHomeDirectory
|
||||
})
|
||||
initialFsGetHomeDirectory = fs.getHomeDirectory;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.getHomeDirectory = initialFsGetHomeDirectory
|
||||
Error.stackTraceLimit = initialStackTraceLimit
|
||||
})
|
||||
fs.getHomeDirectory = initialFsGetHomeDirectory;
|
||||
Error.stackTraceLimit = initialStackTraceLimit;
|
||||
});
|
||||
|
||||
describe('.reportUncaughtException(error)', () => {
|
||||
it('posts errors originated inside Atom Core to BugSnag', () => {
|
||||
const repositoryRootPath = path.join(__dirname, '..')
|
||||
const repositoryRootPath = path.join(__dirname, '..');
|
||||
reporter = new Reporter({
|
||||
request: (url, options) =>
|
||||
requests.push(Object.assign({ url }, options)),
|
||||
alwaysReport: true,
|
||||
reportPreviousErrors: false,
|
||||
resourcePath: repositoryRootPath
|
||||
})
|
||||
});
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
let error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
reporter.reportUncaughtException(error);
|
||||
let [lineNumber, columnNumber] = error.stack
|
||||
.match(/.js:(\d+):(\d+)/)
|
||||
.slice(1)
|
||||
.map(s => parseInt(s))
|
||||
.map(s => parseInt(s));
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe('POST')
|
||||
expect(request.url).toBe('https://notify.bugsnag.com')
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json')
|
||||
let body = JSON.parse(request.body)
|
||||
expect(requests.length).toBe(1);
|
||||
let [request] = requests;
|
||||
expect(request.method).toBe('POST');
|
||||
expect(request.url).toBe('https://notify.bugsnag.com');
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json');
|
||||
let body = JSON.parse(request.body);
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject;
|
||||
|
||||
expect(body).toEqual({
|
||||
apiKey: Reporter.API_KEY,
|
||||
@ -109,29 +109,29 @@ describe('Reporter', () => {
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('posts errors originated outside Atom Core to BugSnag', () => {
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..')
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..');
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
let error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
reporter.reportUncaughtException(error);
|
||||
let [lineNumber, columnNumber] = error.stack
|
||||
.match(/.js:(\d+):(\d+)/)
|
||||
.slice(1)
|
||||
.map(s => parseInt(s))
|
||||
.map(s => parseInt(s));
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe('POST')
|
||||
expect(request.url).toBe('https://notify.bugsnag.com')
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json')
|
||||
let body = JSON.parse(request.body)
|
||||
expect(requests.length).toBe(1);
|
||||
let [request] = requests;
|
||||
expect(request.method).toBe('POST');
|
||||
expect(request.url).toBe('https://notify.bugsnag.com');
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json');
|
||||
let body = JSON.parse(request.body);
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject;
|
||||
|
||||
expect(body).toEqual({
|
||||
apiKey: Reporter.API_KEY,
|
||||
@ -170,84 +170,84 @@ describe('Reporter', () => {
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the error object has `privateMetadata` and `privateMetadataDescription` fields', () => {
|
||||
let [error, notification] = []
|
||||
let [error, notification] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
atom.notifications.clear()
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough()
|
||||
atom.notifications.clear();
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough();
|
||||
|
||||
error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
|
||||
error.metadata = { foo: 'bar' }
|
||||
error.privateMetadata = { baz: 'quux' }
|
||||
error.privateMetadataDescription = 'The contents of baz'
|
||||
})
|
||||
error.metadata = { foo: 'bar' };
|
||||
error.privateMetadata = { baz: 'quux' };
|
||||
error.privateMetadataDescription = 'The contents of baz';
|
||||
});
|
||||
|
||||
it('posts a notification asking for consent', () => {
|
||||
reporter.reportUncaughtException(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
reporter.reportUncaughtException(error);
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('submits the error with the private metadata if the user consents', () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough();
|
||||
reporter.reportUncaughtException(error);
|
||||
reporter.reportUncaughtException.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/)
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1];
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/);
|
||||
|
||||
notificationOptions.buttons[1].onDidClick()
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' })
|
||||
notificationOptions.buttons[1].onDidClick();
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' });
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
expect(notification.isDismissed()).toBe(true);
|
||||
});
|
||||
|
||||
it('submits the error without the private metadata if the user does not consent', () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough();
|
||||
reporter.reportUncaughtException(error);
|
||||
reporter.reportUncaughtException.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/)
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1];
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/);
|
||||
|
||||
notificationOptions.buttons[0].onDidClick()
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar' })
|
||||
notificationOptions.buttons[0].onDidClick();
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar' });
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
expect(notification.isDismissed()).toBe(true);
|
||||
});
|
||||
|
||||
it('submits the error without the private metadata if the user dismisses the notification', () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough();
|
||||
reporter.reportUncaughtException(error);
|
||||
reporter.reportUncaughtException.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification.dismiss()
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
notification.dismiss();
|
||||
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar' })
|
||||
})
|
||||
})
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar' });
|
||||
});
|
||||
});
|
||||
|
||||
it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => {
|
||||
mockActivePackages = [
|
||||
@ -273,71 +273,71 @@ describe('Reporter', () => {
|
||||
'/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2',
|
||||
metadata: { version: '1.2.0' }
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const packageDirPaths = ['/Users/user/.atom/packages']
|
||||
const packageDirPaths = ['/Users/user/.atom/packages'];
|
||||
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths)
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths);
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
let error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
reporter.reportUncaughtException(error);
|
||||
|
||||
expect(error.metadata.userPackages).toEqual({
|
||||
'user-1': '1.0.0',
|
||||
'user-2': '1.2.0'
|
||||
})
|
||||
});
|
||||
expect(error.metadata.bundledPackages).toEqual({
|
||||
'bundled-1': '1.0.0',
|
||||
'bundled-2': '1.2.0'
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('adds previous error messages and assertion failures to the reported metadata', () => {
|
||||
reporter.reportPreviousErrors = true
|
||||
reporter.reportPreviousErrors = true;
|
||||
|
||||
reporter.reportUncaughtException(new Error('A'))
|
||||
reporter.reportUncaughtException(new Error('B'))
|
||||
reporter.reportFailedAssertion(new Error('X'))
|
||||
reporter.reportFailedAssertion(new Error('Y'))
|
||||
reporter.reportUncaughtException(new Error('A'));
|
||||
reporter.reportUncaughtException(new Error('B'));
|
||||
reporter.reportFailedAssertion(new Error('X'));
|
||||
reporter.reportFailedAssertion(new Error('Y'));
|
||||
|
||||
reporter.reportUncaughtException(new Error('C'))
|
||||
reporter.reportUncaughtException(new Error('C'));
|
||||
|
||||
expect(requests.length).toBe(5)
|
||||
expect(requests.length).toBe(5);
|
||||
|
||||
const lastRequest = requests[requests.length - 1]
|
||||
const body = JSON.parse(lastRequest.body)
|
||||
const lastRequest = requests[requests.length - 1];
|
||||
const body = JSON.parse(lastRequest.body);
|
||||
|
||||
console.log(body)
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
|
||||
console.log(body);
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B']);
|
||||
expect(body.events[0].metaData.previousAssertionFailures).toEqual([
|
||||
'X',
|
||||
'Y'
|
||||
])
|
||||
})
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.reportFailedAssertion(error)', () => {
|
||||
it('posts warnings to bugsnag', () => {
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..')
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..');
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportFailedAssertion(error)
|
||||
let error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
reporter.reportFailedAssertion(error);
|
||||
let [lineNumber, columnNumber] = error.stack
|
||||
.match(/.js:(\d+):(\d+)/)
|
||||
.slice(1)
|
||||
.map(s => parseInt(s))
|
||||
.map(s => parseInt(s));
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe('POST')
|
||||
expect(request.url).toBe('https://notify.bugsnag.com')
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json')
|
||||
let body = JSON.parse(request.body)
|
||||
expect(requests.length).toBe(1);
|
||||
let [request] = requests;
|
||||
expect(request.method).toBe('POST');
|
||||
expect(request.url).toBe('https://notify.bugsnag.com');
|
||||
expect(request.headers.get('Content-Type')).toBe('application/json');
|
||||
let body = JSON.parse(request.body);
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject;
|
||||
|
||||
expect(body).toEqual({
|
||||
apiKey: Reporter.API_KEY,
|
||||
@ -376,112 +376,112 @@ describe('Reporter', () => {
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the error object has `privateMetadata` and `privateMetadataDescription` fields', () => {
|
||||
let [error, notification] = []
|
||||
let [error, notification] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
atom.notifications.clear()
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough()
|
||||
atom.notifications.clear();
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough();
|
||||
|
||||
error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
|
||||
error.metadata = { foo: 'bar' }
|
||||
error.privateMetadata = { baz: 'quux' }
|
||||
error.privateMetadataDescription = 'The contents of baz'
|
||||
})
|
||||
error.metadata = { foo: 'bar' };
|
||||
error.privateMetadata = { baz: 'quux' };
|
||||
error.privateMetadataDescription = 'The contents of baz';
|
||||
});
|
||||
|
||||
it('posts a notification asking for consent', () => {
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
reporter.reportFailedAssertion(error);
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('submits the error with the private metadata if the user consents', () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough();
|
||||
reporter.reportFailedAssertion(error);
|
||||
reporter.reportFailedAssertion.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/)
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1];
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/);
|
||||
|
||||
notificationOptions.buttons[1].onDidClick()
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' })
|
||||
notificationOptions.buttons[1].onDidClick();
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar', baz: 'quux' });
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
expect(notification.isDismissed()).toBe(true);
|
||||
});
|
||||
|
||||
it('submits the error without the private metadata if the user does not consent', () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough();
|
||||
reporter.reportFailedAssertion(error);
|
||||
reporter.reportFailedAssertion.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/)
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1];
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/);
|
||||
|
||||
notificationOptions.buttons[0].onDidClick()
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar' })
|
||||
notificationOptions.buttons[0].onDidClick();
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar' });
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
expect(notification.isDismissed()).toBe(true);
|
||||
});
|
||||
|
||||
it('submits the error without the private metadata if the user dismisses the notification', () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough();
|
||||
reporter.reportFailedAssertion(error);
|
||||
reporter.reportFailedAssertion.reset();
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification.dismiss()
|
||||
notification = atom.notifications.getNotifications()[0];
|
||||
notification.dismiss();
|
||||
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({ foo: 'bar' })
|
||||
})
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error);
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1);
|
||||
expect(error.privateMetadata).toBeUndefined();
|
||||
expect(error.privateMetadataDescription).toBeUndefined();
|
||||
expect(error.metadata).toEqual({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it("only notifies the user once for a given 'privateMetadataRequestName'", () => {
|
||||
let fakeStorage = {}
|
||||
let fakeStorage = {};
|
||||
spyOn(global.localStorage, 'setItem').andCallFake(
|
||||
(key, value) => (fakeStorage[key] = value)
|
||||
)
|
||||
);
|
||||
spyOn(global.localStorage, 'getItem').andCallFake(
|
||||
key => fakeStorage[key]
|
||||
)
|
||||
);
|
||||
|
||||
error.privateMetadataRequestName = 'foo'
|
||||
error.privateMetadataRequestName = 'foo';
|
||||
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
atom.notifications.addInfo.reset()
|
||||
reporter.reportFailedAssertion(error);
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled();
|
||||
atom.notifications.addInfo.reset();
|
||||
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).not.toHaveBeenCalled()
|
||||
reporter.reportFailedAssertion(error);
|
||||
expect(atom.notifications.addInfo).not.toHaveBeenCalled();
|
||||
|
||||
let error2 = new Error()
|
||||
Error.captureStackTrace(error2)
|
||||
error2.privateMetadataDescription = 'Something about you'
|
||||
error2.privateMetadata = { baz: 'quux' }
|
||||
error2.privateMetadataRequestName = 'bar'
|
||||
let error2 = new Error();
|
||||
Error.captureStackTrace(error2);
|
||||
error2.privateMetadataDescription = 'Something about you';
|
||||
error2.privateMetadata = { baz: 'quux' };
|
||||
error2.privateMetadataRequestName = 'bar';
|
||||
|
||||
reporter.reportFailedAssertion(error2)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
reporter.reportFailedAssertion(error2);
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => {
|
||||
mockActivePackages = [
|
||||
@ -507,46 +507,46 @@ describe('Reporter', () => {
|
||||
'/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2',
|
||||
metadata: { version: '1.2.0' }
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
const packageDirPaths = ['/Users/user/.atom/packages']
|
||||
const packageDirPaths = ['/Users/user/.atom/packages'];
|
||||
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths)
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths);
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportFailedAssertion(error)
|
||||
let error = new Error();
|
||||
Error.captureStackTrace(error);
|
||||
reporter.reportFailedAssertion(error);
|
||||
|
||||
expect(error.metadata.userPackages).toEqual({
|
||||
'user-1': '1.0.0',
|
||||
'user-2': '1.2.0'
|
||||
})
|
||||
});
|
||||
expect(error.metadata.bundledPackages).toEqual({
|
||||
'bundled-1': '1.0.0',
|
||||
'bundled-2': '1.2.0'
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('adds previous error messages and assertion failures to the reported metadata', () => {
|
||||
reporter.reportPreviousErrors = true
|
||||
reporter.reportPreviousErrors = true;
|
||||
|
||||
reporter.reportUncaughtException(new Error('A'))
|
||||
reporter.reportUncaughtException(new Error('B'))
|
||||
reporter.reportFailedAssertion(new Error('X'))
|
||||
reporter.reportFailedAssertion(new Error('Y'))
|
||||
reporter.reportUncaughtException(new Error('A'));
|
||||
reporter.reportUncaughtException(new Error('B'));
|
||||
reporter.reportFailedAssertion(new Error('X'));
|
||||
reporter.reportFailedAssertion(new Error('Y'));
|
||||
|
||||
reporter.reportFailedAssertion(new Error('C'))
|
||||
reporter.reportFailedAssertion(new Error('C'));
|
||||
|
||||
expect(requests.length).toBe(5)
|
||||
expect(requests.length).toBe(5);
|
||||
|
||||
const lastRequest = requests[requests.length - 1]
|
||||
const body = JSON.parse(lastRequest.body)
|
||||
const lastRequest = requests[requests.length - 1];
|
||||
const body = JSON.parse(lastRequest.body);
|
||||
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B']);
|
||||
expect(body.events[0].metaData.previousAssertionFailures).toEqual([
|
||||
'X',
|
||||
'Y'
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,89 +1,89 @@
|
||||
const SelectListView = require('atom-select-list')
|
||||
const { repositoryForPath } = require('./helpers')
|
||||
const SelectListView = require('atom-select-list');
|
||||
const { repositoryForPath } = require('./helpers');
|
||||
|
||||
module.exports = class DiffListView {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.selectListView = new SelectListView({
|
||||
emptyMessage: 'No diffs in file',
|
||||
items: [],
|
||||
filterKeyForItem: diff => diff.lineText,
|
||||
elementForItem: diff => {
|
||||
const li = document.createElement('li')
|
||||
li.classList.add('two-lines')
|
||||
const li = document.createElement('li');
|
||||
li.classList.add('two-lines');
|
||||
|
||||
const primaryLine = document.createElement('div')
|
||||
primaryLine.classList.add('primary-line')
|
||||
primaryLine.textContent = diff.lineText
|
||||
li.appendChild(primaryLine)
|
||||
const primaryLine = document.createElement('div');
|
||||
primaryLine.classList.add('primary-line');
|
||||
primaryLine.textContent = diff.lineText;
|
||||
li.appendChild(primaryLine);
|
||||
|
||||
const secondaryLine = document.createElement('div')
|
||||
secondaryLine.classList.add('secondary-line')
|
||||
const secondaryLine = document.createElement('div');
|
||||
secondaryLine.classList.add('secondary-line');
|
||||
secondaryLine.textContent = `-${diff.oldStart},${diff.oldLines} +${
|
||||
diff.newStart
|
||||
},${diff.newLines}`
|
||||
li.appendChild(secondaryLine)
|
||||
},${diff.newLines}`;
|
||||
li.appendChild(secondaryLine);
|
||||
|
||||
return li
|
||||
return li;
|
||||
},
|
||||
didConfirmSelection: diff => {
|
||||
this.cancel()
|
||||
const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart
|
||||
this.cancel();
|
||||
const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart;
|
||||
this.editor.setCursorBufferPosition([bufferRow, 0], {
|
||||
autoscroll: true
|
||||
})
|
||||
this.editor.moveToFirstCharacterOfLine()
|
||||
});
|
||||
this.editor.moveToFirstCharacterOfLine();
|
||||
},
|
||||
didCancelSelection: () => {
|
||||
this.cancel()
|
||||
this.cancel();
|
||||
}
|
||||
})
|
||||
this.selectListView.element.classList.add('diff-list-view')
|
||||
});
|
||||
this.selectListView.element.classList.add('diff-list-view');
|
||||
this.panel = atom.workspace.addModalPanel({
|
||||
item: this.selectListView,
|
||||
visible: false
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
attach () {
|
||||
this.previouslyFocusedElement = document.activeElement
|
||||
this.selectListView.reset()
|
||||
this.panel.show()
|
||||
this.selectListView.focus()
|
||||
attach() {
|
||||
this.previouslyFocusedElement = document.activeElement;
|
||||
this.selectListView.reset();
|
||||
this.panel.show();
|
||||
this.selectListView.focus();
|
||||
}
|
||||
|
||||
cancel () {
|
||||
this.panel.hide()
|
||||
cancel() {
|
||||
this.panel.hide();
|
||||
if (this.previouslyFocusedElement) {
|
||||
this.previouslyFocusedElement.focus()
|
||||
this.previouslyFocusedElement = null
|
||||
this.previouslyFocusedElement.focus();
|
||||
this.previouslyFocusedElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.cancel()
|
||||
this.panel.destroy()
|
||||
return this.selectListView.destroy()
|
||||
destroy() {
|
||||
this.cancel();
|
||||
this.panel.destroy();
|
||||
return this.selectListView.destroy();
|
||||
}
|
||||
|
||||
async toggle () {
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
async toggle() {
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
if (this.panel.isVisible()) {
|
||||
this.cancel()
|
||||
this.cancel();
|
||||
} else if (editor) {
|
||||
this.editor = editor
|
||||
const repository = repositoryForPath(this.editor.getPath())
|
||||
this.editor = editor;
|
||||
const repository = repositoryForPath(this.editor.getPath());
|
||||
let diffs = repository
|
||||
? repository.getLineDiffs(this.editor.getPath(), this.editor.getText())
|
||||
: []
|
||||
if (!diffs) diffs = []
|
||||
: [];
|
||||
if (!diffs) diffs = [];
|
||||
for (let diff of diffs) {
|
||||
const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart
|
||||
const lineText = this.editor.lineTextForBufferRow(bufferRow)
|
||||
diff.lineText = lineText ? lineText.trim() : ''
|
||||
const bufferRow = diff.newStart > 0 ? diff.newStart - 1 : diff.newStart;
|
||||
const lineText = this.editor.lineTextForBufferRow(bufferRow);
|
||||
diff.lineText = lineText ? lineText.trim() : '';
|
||||
}
|
||||
|
||||
await this.selectListView.update({ items: diffs })
|
||||
this.attach()
|
||||
await this.selectListView.update({ items: diffs });
|
||||
this.attach();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
const { CompositeDisposable } = require('atom')
|
||||
const { repositoryForPath } = require('./helpers')
|
||||
const { CompositeDisposable } = require('atom');
|
||||
const { repositoryForPath } = require('./helpers');
|
||||
|
||||
const MAX_BUFFER_LENGTH_TO_DIFF = 2 * 1024 * 1024
|
||||
const MAX_BUFFER_LENGTH_TO_DIFF = 2 * 1024 * 1024;
|
||||
|
||||
module.exports = class GitDiffView {
|
||||
constructor (editor) {
|
||||
this.updateDiffs = this.updateDiffs.bind(this)
|
||||
this.editor = editor
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
this.decorations = {}
|
||||
this.markers = []
|
||||
constructor(editor) {
|
||||
this.updateDiffs = this.updateDiffs.bind(this);
|
||||
this.editor = editor;
|
||||
this.subscriptions = new CompositeDisposable();
|
||||
this.decorations = {};
|
||||
this.markers = [];
|
||||
}
|
||||
|
||||
start () {
|
||||
const editorElement = this.editor.getElement()
|
||||
start() {
|
||||
const editorElement = this.editor.getElement();
|
||||
|
||||
this.subscribeToRepository()
|
||||
this.subscribeToRepository();
|
||||
|
||||
this.subscriptions.add(
|
||||
this.editor.onDidStopChanging(this.updateDiffs),
|
||||
@ -35,29 +35,29 @@ module.exports = class GitDiffView {
|
||||
),
|
||||
editorElement.onDidAttach(() => this.updateIconDecoration()),
|
||||
this.editor.onDidDestroy(() => {
|
||||
this.cancelUpdate()
|
||||
this.removeDecorations()
|
||||
this.subscriptions.dispose()
|
||||
this.cancelUpdate();
|
||||
this.removeDecorations();
|
||||
this.subscriptions.dispose();
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
this.updateIconDecoration()
|
||||
this.scheduleUpdate()
|
||||
this.updateIconDecoration();
|
||||
this.scheduleUpdate();
|
||||
}
|
||||
|
||||
moveToNextDiff () {
|
||||
const cursorLineNumber = this.editor.getCursorBufferPosition().row + 1
|
||||
let nextDiffLineNumber = null
|
||||
let firstDiffLineNumber = null
|
||||
moveToNextDiff() {
|
||||
const cursorLineNumber = this.editor.getCursorBufferPosition().row + 1;
|
||||
let nextDiffLineNumber = null;
|
||||
let firstDiffLineNumber = null;
|
||||
if (this.diffs) {
|
||||
for (const { newStart } of this.diffs) {
|
||||
if (newStart > cursorLineNumber) {
|
||||
if (nextDiffLineNumber == null) nextDiffLineNumber = newStart - 1
|
||||
nextDiffLineNumber = Math.min(newStart - 1, nextDiffLineNumber)
|
||||
if (nextDiffLineNumber == null) nextDiffLineNumber = newStart - 1;
|
||||
nextDiffLineNumber = Math.min(newStart - 1, nextDiffLineNumber);
|
||||
}
|
||||
|
||||
if (firstDiffLineNumber == null) firstDiffLineNumber = newStart - 1
|
||||
firstDiffLineNumber = Math.min(newStart - 1, firstDiffLineNumber)
|
||||
if (firstDiffLineNumber == null) firstDiffLineNumber = newStart - 1;
|
||||
firstDiffLineNumber = Math.min(newStart - 1, firstDiffLineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,39 +66,39 @@ module.exports = class GitDiffView {
|
||||
atom.config.get('git-diff.wrapAroundOnMoveToDiff') &&
|
||||
nextDiffLineNumber == null
|
||||
) {
|
||||
nextDiffLineNumber = firstDiffLineNumber
|
||||
nextDiffLineNumber = firstDiffLineNumber;
|
||||
}
|
||||
|
||||
this.moveToLineNumber(nextDiffLineNumber)
|
||||
this.moveToLineNumber(nextDiffLineNumber);
|
||||
}
|
||||
|
||||
updateIconDecoration () {
|
||||
const gutter = this.editor.getElement().querySelector('.gutter')
|
||||
updateIconDecoration() {
|
||||
const gutter = this.editor.getElement().querySelector('.gutter');
|
||||
if (gutter) {
|
||||
if (
|
||||
atom.config.get('editor.showLineNumbers') &&
|
||||
atom.config.get('git-diff.showIconsInEditorGutter')
|
||||
) {
|
||||
gutter.classList.add('git-diff-icon')
|
||||
gutter.classList.add('git-diff-icon');
|
||||
} else {
|
||||
gutter.classList.remove('git-diff-icon')
|
||||
gutter.classList.remove('git-diff-icon');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveToPreviousDiff () {
|
||||
const cursorLineNumber = this.editor.getCursorBufferPosition().row + 1
|
||||
let previousDiffLineNumber = -1
|
||||
let lastDiffLineNumber = -1
|
||||
moveToPreviousDiff() {
|
||||
const cursorLineNumber = this.editor.getCursorBufferPosition().row + 1;
|
||||
let previousDiffLineNumber = -1;
|
||||
let lastDiffLineNumber = -1;
|
||||
if (this.diffs) {
|
||||
for (const { newStart } of this.diffs) {
|
||||
if (newStart < cursorLineNumber) {
|
||||
previousDiffLineNumber = Math.max(
|
||||
newStart - 1,
|
||||
previousDiffLineNumber
|
||||
)
|
||||
);
|
||||
}
|
||||
lastDiffLineNumber = Math.max(newStart - 1, lastDiffLineNumber)
|
||||
lastDiffLineNumber = Math.max(newStart - 1, lastDiffLineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,87 +107,87 @@ module.exports = class GitDiffView {
|
||||
atom.config.get('git-diff.wrapAroundOnMoveToDiff') &&
|
||||
previousDiffLineNumber === -1
|
||||
) {
|
||||
previousDiffLineNumber = lastDiffLineNumber
|
||||
previousDiffLineNumber = lastDiffLineNumber;
|
||||
}
|
||||
|
||||
this.moveToLineNumber(previousDiffLineNumber)
|
||||
this.moveToLineNumber(previousDiffLineNumber);
|
||||
}
|
||||
|
||||
moveToLineNumber (lineNumber) {
|
||||
moveToLineNumber(lineNumber) {
|
||||
if (lineNumber != null && lineNumber >= 0) {
|
||||
this.editor.setCursorBufferPosition([lineNumber, 0])
|
||||
this.editor.moveToFirstCharacterOfLine()
|
||||
this.editor.setCursorBufferPosition([lineNumber, 0]);
|
||||
this.editor.moveToFirstCharacterOfLine();
|
||||
}
|
||||
}
|
||||
|
||||
subscribeToRepository () {
|
||||
this.repository = repositoryForPath(this.editor.getPath())
|
||||
subscribeToRepository() {
|
||||
this.repository = repositoryForPath(this.editor.getPath());
|
||||
if (this.repository) {
|
||||
this.subscriptions.add(
|
||||
this.repository.onDidChangeStatuses(() => {
|
||||
this.scheduleUpdate()
|
||||
this.scheduleUpdate();
|
||||
})
|
||||
)
|
||||
);
|
||||
this.subscriptions.add(
|
||||
this.repository.onDidChangeStatus(changedPath => {
|
||||
if (changedPath === this.editor.getPath()) this.scheduleUpdate()
|
||||
if (changedPath === this.editor.getPath()) this.scheduleUpdate();
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
cancelUpdate () {
|
||||
clearImmediate(this.immediateId)
|
||||
cancelUpdate() {
|
||||
clearImmediate(this.immediateId);
|
||||
}
|
||||
|
||||
scheduleUpdate () {
|
||||
this.cancelUpdate()
|
||||
this.immediateId = setImmediate(this.updateDiffs)
|
||||
scheduleUpdate() {
|
||||
this.cancelUpdate();
|
||||
this.immediateId = setImmediate(this.updateDiffs);
|
||||
}
|
||||
|
||||
updateDiffs () {
|
||||
if (this.editor.isDestroyed()) return
|
||||
this.removeDecorations()
|
||||
const path = this.editor && this.editor.getPath()
|
||||
updateDiffs() {
|
||||
if (this.editor.isDestroyed()) return;
|
||||
this.removeDecorations();
|
||||
const path = this.editor && this.editor.getPath();
|
||||
if (
|
||||
path &&
|
||||
this.editor.getBuffer().getLength() < MAX_BUFFER_LENGTH_TO_DIFF
|
||||
) {
|
||||
this.diffs =
|
||||
this.repository &&
|
||||
this.repository.getLineDiffs(path, this.editor.getText())
|
||||
if (this.diffs) this.addDecorations(this.diffs)
|
||||
this.repository.getLineDiffs(path, this.editor.getText());
|
||||
if (this.diffs) this.addDecorations(this.diffs);
|
||||
}
|
||||
}
|
||||
|
||||
addDecorations (diffs) {
|
||||
addDecorations(diffs) {
|
||||
for (const { newStart, oldLines, newLines } of diffs) {
|
||||
const startRow = newStart - 1
|
||||
const endRow = newStart + newLines - 1
|
||||
const startRow = newStart - 1;
|
||||
const endRow = newStart + newLines - 1;
|
||||
if (oldLines === 0 && newLines > 0) {
|
||||
this.markRange(startRow, endRow, 'git-line-added')
|
||||
this.markRange(startRow, endRow, 'git-line-added');
|
||||
} else if (newLines === 0 && oldLines > 0) {
|
||||
if (startRow < 0) {
|
||||
this.markRange(0, 0, 'git-previous-line-removed')
|
||||
this.markRange(0, 0, 'git-previous-line-removed');
|
||||
} else {
|
||||
this.markRange(startRow, startRow, 'git-line-removed')
|
||||
this.markRange(startRow, startRow, 'git-line-removed');
|
||||
}
|
||||
} else {
|
||||
this.markRange(startRow, endRow, 'git-line-modified')
|
||||
this.markRange(startRow, endRow, 'git-line-modified');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeDecorations () {
|
||||
for (let marker of this.markers) marker.destroy()
|
||||
this.markers = []
|
||||
removeDecorations() {
|
||||
for (let marker of this.markers) marker.destroy();
|
||||
this.markers = [];
|
||||
}
|
||||
|
||||
markRange (startRow, endRow, klass) {
|
||||
markRange(startRow, endRow, klass) {
|
||||
const marker = this.editor.markBufferRange([[startRow, 0], [endRow, 0]], {
|
||||
invalidate: 'never'
|
||||
})
|
||||
this.editor.decorateMarker(marker, { type: 'line-number', class: klass })
|
||||
this.markers.push(marker)
|
||||
});
|
||||
this.editor.decorateMarker(marker, { type: 'line-number', class: klass });
|
||||
this.markers.push(marker);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
exports.repositoryForPath = function (goalPath) {
|
||||
const directories = atom.project.getDirectories()
|
||||
const repositories = atom.project.getRepositories()
|
||||
exports.repositoryForPath = function(goalPath) {
|
||||
const directories = atom.project.getDirectories();
|
||||
const repositories = atom.project.getRepositories();
|
||||
for (let i = 0; i < directories.length; i++) {
|
||||
const directory = directories[i]
|
||||
const directory = directories[i];
|
||||
if (goalPath === directory.getPath() || directory.contains(goalPath)) {
|
||||
return repositories[i]
|
||||
return repositories[i];
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -1,32 +1,32 @@
|
||||
const GitDiffView = require('./git-diff-view')
|
||||
const DiffListView = require('./diff-list-view')
|
||||
const GitDiffView = require('./git-diff-view');
|
||||
const DiffListView = require('./diff-list-view');
|
||||
|
||||
let diffListView = null
|
||||
let diffListView = null;
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
const watchedEditors = new WeakSet()
|
||||
activate() {
|
||||
const watchedEditors = new WeakSet();
|
||||
|
||||
atom.workspace.observeTextEditors(editor => {
|
||||
if (watchedEditors.has(editor)) return
|
||||
if (watchedEditors.has(editor)) return;
|
||||
|
||||
new GitDiffView(editor).start()
|
||||
new GitDiffView(editor).start();
|
||||
atom.commands.add(
|
||||
atom.views.getView(editor),
|
||||
'git-diff:toggle-diff-list',
|
||||
() => {
|
||||
if (diffListView == null) diffListView = new DiffListView()
|
||||
diffListView.toggle()
|
||||
if (diffListView == null) diffListView = new DiffListView();
|
||||
diffListView.toggle();
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
watchedEditors.add(editor)
|
||||
editor.onDidDestroy(() => watchedEditors.delete(editor))
|
||||
})
|
||||
watchedEditors.add(editor);
|
||||
editor.onDidDestroy(() => watchedEditors.delete(editor));
|
||||
});
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
if (diffListView) diffListView.destroy()
|
||||
diffListView = null
|
||||
deactivate() {
|
||||
if (diffListView) diffListView.destroy();
|
||||
diffListView = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,49 +1,51 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
const temp = require('temp')
|
||||
const path = require('path');
|
||||
const fs = require('fs-plus');
|
||||
const temp = require('temp');
|
||||
|
||||
describe('git-diff:toggle-diff-list', () => {
|
||||
let diffListView, editor
|
||||
let diffListView, editor;
|
||||
|
||||
beforeEach(() => {
|
||||
const projectPath = temp.mkdirSync('git-diff-spec-')
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath)
|
||||
const projectPath = temp.mkdirSync('git-diff-spec-');
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath);
|
||||
fs.moveSync(
|
||||
path.join(projectPath, 'git.git'),
|
||||
path.join(projectPath, '.git')
|
||||
)
|
||||
atom.project.setPaths([projectPath])
|
||||
);
|
||||
atom.project.setPaths([projectPath]);
|
||||
|
||||
jasmine.attachToDOM(atom.workspace.getElement())
|
||||
jasmine.attachToDOM(atom.workspace.getElement());
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('git-diff'))
|
||||
waitsForPromise(() => atom.packages.activatePackage('git-diff'));
|
||||
|
||||
waitsForPromise(() => atom.workspace.open('sample.js'))
|
||||
waitsForPromise(() => atom.workspace.open('sample.js'));
|
||||
|
||||
runs(() => {
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editor.setCursorBufferPosition([8, 30])
|
||||
editor.insertText('a')
|
||||
atom.commands.dispatch(editor.getElement(), 'git-diff:toggle-diff-list')
|
||||
})
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setCursorBufferPosition([8, 30]);
|
||||
editor.insertText('a');
|
||||
atom.commands.dispatch(editor.getElement(), 'git-diff:toggle-diff-list');
|
||||
});
|
||||
|
||||
waitsFor(() => {
|
||||
diffListView = document.querySelector('.diff-list-view')
|
||||
return diffListView && diffListView.querySelectorAll('li').length > 0
|
||||
})
|
||||
})
|
||||
diffListView = document.querySelector('.diff-list-view');
|
||||
return diffListView && diffListView.querySelectorAll('li').length > 0;
|
||||
});
|
||||
});
|
||||
|
||||
it('shows a list of all diff hunks', () => {
|
||||
diffListView = document.querySelector('.diff-list-view ol')
|
||||
expect(diffListView.textContent).toBe('while (items.length > 0) {a-9,1 +9,1')
|
||||
})
|
||||
diffListView = document.querySelector('.diff-list-view ol');
|
||||
expect(diffListView.textContent).toBe(
|
||||
'while (items.length > 0) {a-9,1 +9,1'
|
||||
);
|
||||
});
|
||||
|
||||
it('moves the cursor to the selected hunk', () => {
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setCursorBufferPosition([0, 0]);
|
||||
atom.commands.dispatch(
|
||||
document.querySelector('.diff-list-view'),
|
||||
'core:confirm'
|
||||
)
|
||||
expect(editor.getCursorBufferPosition()).toEqual([8, 4])
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(editor.getCursorBufferPosition()).toEqual([8, 4]);
|
||||
});
|
||||
});
|
||||
|
@ -1,244 +1,246 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
const temp = require('temp')
|
||||
const path = require('path');
|
||||
const fs = require('fs-plus');
|
||||
const temp = require('temp');
|
||||
|
||||
describe('GitDiff package', () => {
|
||||
let editor, editorElement, projectPath
|
||||
let editor, editorElement, projectPath;
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(window, 'setImmediate').andCallFake(fn => fn())
|
||||
spyOn(window, 'setImmediate').andCallFake(fn => fn());
|
||||
|
||||
projectPath = temp.mkdirSync('git-diff-spec-')
|
||||
const otherPath = temp.mkdirSync('some-other-path-')
|
||||
projectPath = temp.mkdirSync('git-diff-spec-');
|
||||
const otherPath = temp.mkdirSync('some-other-path-');
|
||||
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath)
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'working-dir'), projectPath);
|
||||
fs.moveSync(
|
||||
path.join(projectPath, 'git.git'),
|
||||
path.join(projectPath, '.git')
|
||||
)
|
||||
atom.project.setPaths([otherPath, projectPath])
|
||||
);
|
||||
atom.project.setPaths([otherPath, projectPath]);
|
||||
|
||||
jasmine.attachToDOM(atom.workspace.getElement())
|
||||
jasmine.attachToDOM(atom.workspace.getElement());
|
||||
|
||||
waitsForPromise(() =>
|
||||
atom.workspace.open(path.join(projectPath, 'sample.js'))
|
||||
)
|
||||
);
|
||||
|
||||
runs(() => {
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editorElement = editor.getElement()
|
||||
})
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editorElement = editor.getElement();
|
||||
});
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('git-diff'))
|
||||
})
|
||||
waitsForPromise(() => atom.packages.activatePackage('git-diff'));
|
||||
});
|
||||
|
||||
describe('when the editor has modified lines', () => {
|
||||
it('highlights the modified lines', () => {
|
||||
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
||||
0
|
||||
)
|
||||
editor.insertText('a')
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
);
|
||||
editor.insertText('a');
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
||||
1
|
||||
)
|
||||
);
|
||||
expect(editorElement.querySelector('.git-line-modified')).toHaveData(
|
||||
'buffer-row',
|
||||
0
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the editor has added lines', () => {
|
||||
it('highlights the added lines', () => {
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0)
|
||||
editor.moveToEndOfLine()
|
||||
editor.insertNewline()
|
||||
editor.insertText('a')
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(1)
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
||||
editor.moveToEndOfLine();
|
||||
editor.insertNewline();
|
||||
editor.insertText('a');
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(1);
|
||||
expect(editorElement.querySelector('.git-line-added')).toHaveData(
|
||||
'buffer-row',
|
||||
1
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the editor has removed lines', () => {
|
||||
it('highlights the line preceeding the deleted lines', () => {
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0)
|
||||
editor.setCursorBufferPosition([5])
|
||||
editor.deleteLine()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
expect(editorElement.querySelectorAll('.git-line-removed').length).toBe(1)
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
||||
editor.setCursorBufferPosition([5]);
|
||||
editor.deleteLine();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(editorElement.querySelectorAll('.git-line-removed').length).toBe(
|
||||
1
|
||||
);
|
||||
expect(editorElement.querySelector('.git-line-removed')).toHaveData(
|
||||
'buffer-row',
|
||||
4
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the editor has removed the first line', () => {
|
||||
it('highlights the line preceeding the deleted lines', () => {
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0)
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.deleteLine()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
expect(editorElement.querySelectorAll('.git-line-added').length).toBe(0);
|
||||
editor.setCursorBufferPosition([0, 0]);
|
||||
editor.deleteLine();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(
|
||||
editorElement.querySelectorAll('.git-previous-line-removed').length
|
||||
).toBe(1)
|
||||
).toBe(1);
|
||||
expect(
|
||||
editorElement.querySelector('.git-previous-line-removed')
|
||||
).toHaveData('buffer-row', 0)
|
||||
})
|
||||
})
|
||||
).toHaveData('buffer-row', 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a modified line is restored to the HEAD version contents', () => {
|
||||
it('removes the diff highlight', () => {
|
||||
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
||||
0
|
||||
)
|
||||
editor.insertText('a')
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
);
|
||||
editor.insertText('a');
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
||||
1
|
||||
)
|
||||
editor.backspace()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
);
|
||||
editor.backspace();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
expect(editorElement.querySelectorAll('.git-line-modified').length).toBe(
|
||||
0
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a modified file is opened', () => {
|
||||
it('highlights the changed lines', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(projectPath, 'sample.txt'),
|
||||
'Some different text.'
|
||||
)
|
||||
let nextTick = false
|
||||
);
|
||||
let nextTick = false;
|
||||
|
||||
waitsForPromise(() =>
|
||||
atom.workspace.open(path.join(projectPath, 'sample.txt'))
|
||||
)
|
||||
);
|
||||
|
||||
runs(() => {
|
||||
editorElement = atom.workspace.getActiveTextEditor().getElement()
|
||||
})
|
||||
editorElement = atom.workspace.getActiveTextEditor().getElement();
|
||||
});
|
||||
|
||||
setImmediate(() => {
|
||||
nextTick = true
|
||||
})
|
||||
nextTick = true;
|
||||
});
|
||||
|
||||
waitsFor(() => nextTick)
|
||||
waitsFor(() => nextTick);
|
||||
|
||||
runs(() => {
|
||||
expect(
|
||||
editorElement.querySelectorAll('.git-line-modified').length
|
||||
).toBe(1)
|
||||
).toBe(1);
|
||||
expect(editorElement.querySelector('.git-line-modified')).toHaveData(
|
||||
'buffer-row',
|
||||
0
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the project paths change', () => {
|
||||
it("doesn't try to use the destroyed git repository", () => {
|
||||
editor.deleteLine()
|
||||
atom.project.setPaths([temp.mkdirSync('no-repository')])
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
})
|
||||
})
|
||||
editor.deleteLine();
|
||||
atom.project.setPaths([temp.mkdirSync('no-repository')]);
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
});
|
||||
});
|
||||
|
||||
describe('move-to-next-diff/move-to-previous-diff events', () => {
|
||||
it('moves the cursor to first character of the next/previous diff line', () => {
|
||||
editor.insertText('a')
|
||||
editor.setCursorBufferPosition([5])
|
||||
editor.deleteLine()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
editor.insertText('a');
|
||||
editor.setCursorBufferPosition([5]);
|
||||
editor.deleteLine();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
editor.setCursorBufferPosition([0])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4])
|
||||
editor.setCursorBufferPosition([0]);
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
|
||||
})
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
||||
});
|
||||
|
||||
it('wraps around to the first/last diff in the file', () => {
|
||||
editor.insertText('a')
|
||||
editor.setCursorBufferPosition([5])
|
||||
editor.deleteLine()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
editor.insertText('a');
|
||||
editor.setCursorBufferPosition([5]);
|
||||
editor.deleteLine();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
editor.setCursorBufferPosition([0])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4])
|
||||
editor.setCursorBufferPosition([0]);
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4])
|
||||
})
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
||||
});
|
||||
|
||||
describe('when the wrapAroundOnMoveToDiff config option is false', () => {
|
||||
beforeEach(() =>
|
||||
atom.config.set('git-diff.wrapAroundOnMoveToDiff', false)
|
||||
)
|
||||
);
|
||||
|
||||
it('does not wraps around to the first/last diff in the file', () => {
|
||||
editor.insertText('a')
|
||||
editor.setCursorBufferPosition([5])
|
||||
editor.deleteLine()
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay)
|
||||
editor.insertText('a');
|
||||
editor.setCursorBufferPosition([5]);
|
||||
editor.deleteLine();
|
||||
advanceClock(editor.getBuffer().stoppedChangingDelay);
|
||||
|
||||
editor.setCursorBufferPosition([0])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4])
|
||||
editor.setCursorBufferPosition([0]);
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-next-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([4, 4]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
||||
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0])
|
||||
})
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorElement, 'git-diff:move-to-previous-diff');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([0, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the showIconsInEditorGutter config option is true', () => {
|
||||
beforeEach(() => {
|
||||
atom.config.set('git-diff.showIconsInEditorGutter', true)
|
||||
})
|
||||
atom.config.set('git-diff.showIconsInEditorGutter', true);
|
||||
});
|
||||
|
||||
it('the gutter has a git-diff-icon class', () =>
|
||||
expect(editorElement.querySelector('.gutter')).toHaveClass(
|
||||
'git-diff-icon'
|
||||
))
|
||||
));
|
||||
|
||||
it('keeps the git-diff-icon class when editor.showLineNumbers is toggled', () => {
|
||||
atom.config.set('editor.showLineNumbers', false)
|
||||
atom.config.set('editor.showLineNumbers', false);
|
||||
expect(editorElement.querySelector('.gutter')).not.toHaveClass(
|
||||
'git-diff-icon'
|
||||
)
|
||||
);
|
||||
|
||||
atom.config.set('editor.showLineNumbers', true)
|
||||
atom.config.set('editor.showLineNumbers', true);
|
||||
expect(editorElement.querySelector('.gutter')).toHaveClass(
|
||||
'git-diff-icon'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('removes the git-diff-icon class when the showIconsInEditorGutter config option set to false', () => {
|
||||
atom.config.set('git-diff.showIconsInEditorGutter', false)
|
||||
atom.config.set('git-diff.showIconsInEditorGutter', false);
|
||||
expect(editorElement.querySelector('.gutter')).not.toHaveClass(
|
||||
'git-diff-icon'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,111 +1,111 @@
|
||||
'use babel'
|
||||
'use babel';
|
||||
|
||||
import { Point, TextEditor } from 'atom'
|
||||
import { Point, TextEditor } from 'atom';
|
||||
|
||||
class GoToLineView {
|
||||
constructor () {
|
||||
this.miniEditor = new TextEditor({ mini: true })
|
||||
this.miniEditor.element.addEventListener('blur', this.close.bind(this))
|
||||
constructor() {
|
||||
this.miniEditor = new TextEditor({ mini: true });
|
||||
this.miniEditor.element.addEventListener('blur', this.close.bind(this));
|
||||
|
||||
this.message = document.createElement('div')
|
||||
this.message.classList.add('message')
|
||||
this.message = document.createElement('div');
|
||||
this.message.classList.add('message');
|
||||
|
||||
this.element = document.createElement('div')
|
||||
this.element.classList.add('go-to-line')
|
||||
this.element.appendChild(this.miniEditor.element)
|
||||
this.element.appendChild(this.message)
|
||||
this.element = document.createElement('div');
|
||||
this.element.classList.add('go-to-line');
|
||||
this.element.appendChild(this.miniEditor.element);
|
||||
this.element.appendChild(this.message);
|
||||
|
||||
this.panel = atom.workspace.addModalPanel({
|
||||
item: this,
|
||||
visible: false
|
||||
})
|
||||
});
|
||||
atom.commands.add('atom-text-editor', 'go-to-line:toggle', () => {
|
||||
this.toggle()
|
||||
return false
|
||||
})
|
||||
this.toggle();
|
||||
return false;
|
||||
});
|
||||
atom.commands.add(this.miniEditor.element, 'core:confirm', () => {
|
||||
this.navigate()
|
||||
})
|
||||
this.navigate();
|
||||
});
|
||||
atom.commands.add(this.miniEditor.element, 'core:cancel', () => {
|
||||
this.close()
|
||||
})
|
||||
this.close();
|
||||
});
|
||||
this.miniEditor.onWillInsertText(arg => {
|
||||
if (arg.text.match(/[^0-9:]/)) {
|
||||
arg.cancel()
|
||||
arg.cancel();
|
||||
}
|
||||
})
|
||||
});
|
||||
this.miniEditor.onDidChange(() => {
|
||||
this.navigate({ keepOpen: true })
|
||||
})
|
||||
this.navigate({ keepOpen: true });
|
||||
});
|
||||
}
|
||||
|
||||
toggle () {
|
||||
this.panel.isVisible() ? this.close() : this.open()
|
||||
toggle() {
|
||||
this.panel.isVisible() ? this.close() : this.open();
|
||||
}
|
||||
|
||||
close () {
|
||||
if (!this.panel.isVisible()) return
|
||||
this.miniEditor.setText('')
|
||||
this.panel.hide()
|
||||
close() {
|
||||
if (!this.panel.isVisible()) return;
|
||||
this.miniEditor.setText('');
|
||||
this.panel.hide();
|
||||
if (this.miniEditor.element.hasFocus()) {
|
||||
this.restoreFocus()
|
||||
this.restoreFocus();
|
||||
}
|
||||
}
|
||||
|
||||
navigate (options = {}) {
|
||||
const lineNumber = this.miniEditor.getText()
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
navigate(options = {}) {
|
||||
const lineNumber = this.miniEditor.getText();
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
if (!options.keepOpen) {
|
||||
this.close()
|
||||
this.close();
|
||||
}
|
||||
if (!editor || !lineNumber.length) return
|
||||
if (!editor || !lineNumber.length) return;
|
||||
|
||||
const currentRow = editor.getCursorBufferPosition().row
|
||||
const rowLineNumber = lineNumber.split(/:+/)[0] || ''
|
||||
const currentRow = editor.getCursorBufferPosition().row;
|
||||
const rowLineNumber = lineNumber.split(/:+/)[0] || '';
|
||||
const row =
|
||||
rowLineNumber.length > 0 ? parseInt(rowLineNumber) - 1 : currentRow
|
||||
const columnLineNumber = lineNumber.split(/:+/)[1] || ''
|
||||
rowLineNumber.length > 0 ? parseInt(rowLineNumber) - 1 : currentRow;
|
||||
const columnLineNumber = lineNumber.split(/:+/)[1] || '';
|
||||
const column =
|
||||
columnLineNumber.length > 0 ? parseInt(columnLineNumber) - 1 : -1
|
||||
columnLineNumber.length > 0 ? parseInt(columnLineNumber) - 1 : -1;
|
||||
|
||||
const position = new Point(row, column)
|
||||
editor.setCursorBufferPosition(position)
|
||||
editor.unfoldBufferRow(row)
|
||||
const position = new Point(row, column);
|
||||
editor.setCursorBufferPosition(position);
|
||||
editor.unfoldBufferRow(row);
|
||||
if (column < 0) {
|
||||
editor.moveToFirstCharacterOfLine()
|
||||
editor.moveToFirstCharacterOfLine();
|
||||
}
|
||||
editor.scrollToBufferPosition(position, {
|
||||
center: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
storeFocusedElement () {
|
||||
this.previouslyFocusedElement = document.activeElement
|
||||
return this.previouslyFocusedElement
|
||||
storeFocusedElement() {
|
||||
this.previouslyFocusedElement = document.activeElement;
|
||||
return this.previouslyFocusedElement;
|
||||
}
|
||||
|
||||
restoreFocus () {
|
||||
restoreFocus() {
|
||||
if (
|
||||
this.previouslyFocusedElement &&
|
||||
this.previouslyFocusedElement.parentElement
|
||||
) {
|
||||
return this.previouslyFocusedElement.focus()
|
||||
return this.previouslyFocusedElement.focus();
|
||||
}
|
||||
atom.views.getView(atom.workspace).focus()
|
||||
atom.views.getView(atom.workspace).focus();
|
||||
}
|
||||
|
||||
open () {
|
||||
if (this.panel.isVisible() || !atom.workspace.getActiveTextEditor()) return
|
||||
this.storeFocusedElement()
|
||||
this.panel.show()
|
||||
open() {
|
||||
if (this.panel.isVisible() || !atom.workspace.getActiveTextEditor()) return;
|
||||
this.storeFocusedElement();
|
||||
this.panel.show();
|
||||
this.message.textContent =
|
||||
'Enter a <row> or <row>:<column> to go there. Examples: "3" for row 3 or "2:7" for row 2 and column 7'
|
||||
this.miniEditor.element.focus()
|
||||
'Enter a <row> or <row>:<column> to go there. Examples: "3" for row 3 or "2:7" for row 2 and column 7';
|
||||
this.miniEditor.element.focus();
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
activate () {
|
||||
return new GoToLineView()
|
||||
activate() {
|
||||
return new GoToLineView();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,169 +1,169 @@
|
||||
'use babel'
|
||||
'use babel';
|
||||
|
||||
/* eslint-env jasmine */
|
||||
|
||||
import GoToLineView from '../lib/go-to-line-view'
|
||||
import GoToLineView from '../lib/go-to-line-view';
|
||||
|
||||
describe('GoToLine', () => {
|
||||
let editor = null
|
||||
let editorView = null
|
||||
let goToLine = null
|
||||
let editor = null;
|
||||
let editorView = null;
|
||||
let goToLine = null;
|
||||
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() => {
|
||||
return atom.workspace.open('sample.js')
|
||||
})
|
||||
return atom.workspace.open('sample.js');
|
||||
});
|
||||
|
||||
runs(() => {
|
||||
const workspaceElement = atom.views.getView(atom.workspace)
|
||||
workspaceElement.style.height = '200px'
|
||||
workspaceElement.style.width = '1000px'
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editorView = atom.views.getView(editor)
|
||||
goToLine = GoToLineView.activate()
|
||||
editor.setCursorBufferPosition([1, 0])
|
||||
})
|
||||
})
|
||||
const workspaceElement = atom.views.getView(atom.workspace);
|
||||
workspaceElement.style.height = '200px';
|
||||
workspaceElement.style.width = '1000px';
|
||||
jasmine.attachToDOM(workspaceElement);
|
||||
editor = atom.workspace.getActiveTextEditor();
|
||||
editorView = atom.views.getView(editor);
|
||||
goToLine = GoToLineView.activate();
|
||||
editor.setCursorBufferPosition([1, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when go-to-line:toggle is triggered', () => {
|
||||
it('adds a modal panel', () => {
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when entering a line number', () => {
|
||||
it('only allows 0-9 and the colon character to be entered in the mini editor', () => {
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText('a')
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText('path/file.txt:56')
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText(':')
|
||||
expect(goToLine.miniEditor.getText()).toBe(':')
|
||||
goToLine.miniEditor.setText('')
|
||||
goToLine.miniEditor.insertText('4')
|
||||
expect(goToLine.miniEditor.getText()).toBe('4')
|
||||
})
|
||||
})
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText('a');
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText('path/file.txt:56');
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText(':');
|
||||
expect(goToLine.miniEditor.getText()).toBe(':');
|
||||
goToLine.miniEditor.setText('');
|
||||
goToLine.miniEditor.insertText('4');
|
||||
expect(goToLine.miniEditor.getText()).toBe('4');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when typing line numbers (auto-navigation)', () => {
|
||||
it('automatically scrolls to the desired line', () => {
|
||||
goToLine.miniEditor.insertText('19')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([18, 0])
|
||||
})
|
||||
})
|
||||
goToLine.miniEditor.insertText('19');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([18, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when typing line and column numbers (auto-navigation)', () => {
|
||||
it('automatically scrolls to the desired line and column', () => {
|
||||
goToLine.miniEditor.insertText('3:8')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 7])
|
||||
})
|
||||
})
|
||||
goToLine.miniEditor.insertText('3:8');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 7]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when entering a line number and column number', () => {
|
||||
it('moves the cursor to the column number of the line specified', () => {
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText('3:14')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 13])
|
||||
})
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText('3:14');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 13]);
|
||||
});
|
||||
|
||||
it('centers the selected line', () => {
|
||||
goToLine.miniEditor.insertText('45:4')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
const rowsPerPage = editor.getRowsPerPage()
|
||||
const currentRow = editor.getCursorBufferPosition().row - 1
|
||||
goToLine.miniEditor.insertText('45:4');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
const rowsPerPage = editor.getRowsPerPage();
|
||||
const currentRow = editor.getCursorBufferPosition().row - 1;
|
||||
expect(editor.getFirstVisibleScreenRow()).toBe(
|
||||
currentRow - Math.ceil(rowsPerPage / 2)
|
||||
)
|
||||
);
|
||||
expect(editor.getLastVisibleScreenRow()).toBe(
|
||||
currentRow + Math.floor(rowsPerPage / 2)
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when entering a line number greater than the number of rows in the buffer', () => {
|
||||
it('moves the cursor position to the first character of the last line', () => {
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText('78')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([77, 0])
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText('78');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([77, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when entering a column number greater than the number in the specified line', () => {
|
||||
it('moves the cursor position to the last character of the specified line', () => {
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
expect(goToLine.miniEditor.getText()).toBe('')
|
||||
goToLine.miniEditor.insertText('3:43')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 39])
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
expect(goToLine.miniEditor.getText()).toBe('');
|
||||
goToLine.miniEditor.insertText('3:43');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 39]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when core:confirm is triggered', () => {
|
||||
describe('when a line number has been entered', () => {
|
||||
it('moves the cursor to the first character of the line', () => {
|
||||
goToLine.miniEditor.insertText('3')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 4])
|
||||
})
|
||||
})
|
||||
goToLine.miniEditor.insertText('3');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([2, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the line number entered is nested within foldes', () => {
|
||||
it('unfolds all folds containing the given row', () => {
|
||||
expect(editor.indentationForBufferRow(9)).toEqual(3)
|
||||
editor.foldAll()
|
||||
expect(editor.screenRowForBufferRow(9)).toEqual(0)
|
||||
goToLine.miniEditor.insertText('10')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(editor.getCursorBufferPosition()).toEqual([9, 6])
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(editor.indentationForBufferRow(9)).toEqual(3);
|
||||
editor.foldAll();
|
||||
expect(editor.screenRowForBufferRow(9)).toEqual(0);
|
||||
goToLine.miniEditor.insertText('10');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(editor.getCursorBufferPosition()).toEqual([9, 6]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no line number has been entered', () => {
|
||||
it('closes the view and does not update the cursor position', () => {
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([1, 0])
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([1, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when no line number has been entered, but a column number has been entered', () => {
|
||||
it('navigates to the column of the current line', () => {
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
goToLine.miniEditor.insertText('4:1')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([3, 0])
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
goToLine.miniEditor.insertText(':19')
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([3, 18])
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
goToLine.miniEditor.insertText('4:1');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([3, 0]);
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
goToLine.miniEditor.insertText(':19');
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:confirm');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([3, 18]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when core:cancel is triggered', () => {
|
||||
it('closes the view and does not update the cursor position', () => {
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle')
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy()
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:cancel')
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy()
|
||||
expect(editor.getCursorBufferPosition()).toEqual([1, 0])
|
||||
})
|
||||
})
|
||||
})
|
||||
atom.commands.dispatch(editorView, 'go-to-line:toggle');
|
||||
expect(goToLine.panel.isVisible()).toBeTruthy();
|
||||
atom.commands.dispatch(goToLine.miniEditor.element, 'core:cancel');
|
||||
expect(goToLine.panel.isVisible()).toBeFalsy();
|
||||
expect(editor.getCursorBufferPosition()).toEqual([1, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,103 +1,103 @@
|
||||
const SelectListView = require('atom-select-list')
|
||||
const SelectListView = require('atom-select-list');
|
||||
|
||||
module.exports = class GrammarListView {
|
||||
constructor () {
|
||||
this.autoDetect = { name: 'Auto Detect' }
|
||||
constructor() {
|
||||
this.autoDetect = { name: 'Auto Detect' };
|
||||
this.selectListView = new SelectListView({
|
||||
itemsClassList: ['mark-active'],
|
||||
items: [],
|
||||
filterKeyForItem: grammar => grammar.name,
|
||||
elementForItem: grammar => {
|
||||
const grammarName = grammar.name || grammar.scopeName
|
||||
const element = document.createElement('li')
|
||||
const grammarName = grammar.name || grammar.scopeName;
|
||||
const element = document.createElement('li');
|
||||
if (grammar === this.currentGrammar) {
|
||||
element.classList.add('active')
|
||||
element.classList.add('active');
|
||||
}
|
||||
element.textContent = grammarName
|
||||
element.dataset.grammar = grammarName
|
||||
element.textContent = grammarName;
|
||||
element.dataset.grammar = grammarName;
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('pull-right')
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('pull-right');
|
||||
if (grammar.scopeName) {
|
||||
const scopeName = document.createElement('scopeName')
|
||||
scopeName.classList.add('key-binding') // It will be styled the same as the keybindings in the command palette
|
||||
scopeName.textContent = grammar.scopeName
|
||||
div.appendChild(scopeName)
|
||||
element.appendChild(div)
|
||||
const scopeName = document.createElement('scopeName');
|
||||
scopeName.classList.add('key-binding'); // It will be styled the same as the keybindings in the command palette
|
||||
scopeName.textContent = grammar.scopeName;
|
||||
div.appendChild(scopeName);
|
||||
element.appendChild(div);
|
||||
}
|
||||
|
||||
return element
|
||||
return element;
|
||||
},
|
||||
didConfirmSelection: grammar => {
|
||||
this.cancel()
|
||||
this.cancel();
|
||||
if (grammar === this.autoDetect) {
|
||||
atom.textEditors.clearGrammarOverride(this.editor)
|
||||
atom.textEditors.clearGrammarOverride(this.editor);
|
||||
} else {
|
||||
atom.textEditors.setGrammarOverride(this.editor, grammar.scopeName)
|
||||
atom.textEditors.setGrammarOverride(this.editor, grammar.scopeName);
|
||||
}
|
||||
},
|
||||
didCancelSelection: () => {
|
||||
this.cancel()
|
||||
this.cancel();
|
||||
}
|
||||
})
|
||||
this.selectListView.element.classList.add('grammar-selector')
|
||||
});
|
||||
this.selectListView.element.classList.add('grammar-selector');
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.cancel()
|
||||
return this.selectListView.destroy()
|
||||
destroy() {
|
||||
this.cancel();
|
||||
return this.selectListView.destroy();
|
||||
}
|
||||
|
||||
cancel () {
|
||||
cancel() {
|
||||
if (this.panel != null) {
|
||||
this.panel.destroy()
|
||||
this.panel.destroy();
|
||||
}
|
||||
this.panel = null
|
||||
this.currentGrammar = null
|
||||
this.panel = null;
|
||||
this.currentGrammar = null;
|
||||
if (this.previouslyFocusedElement) {
|
||||
this.previouslyFocusedElement.focus()
|
||||
this.previouslyFocusedElement = null
|
||||
this.previouslyFocusedElement.focus();
|
||||
this.previouslyFocusedElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
attach () {
|
||||
this.previouslyFocusedElement = document.activeElement
|
||||
attach() {
|
||||
this.previouslyFocusedElement = document.activeElement;
|
||||
if (this.panel == null) {
|
||||
this.panel = atom.workspace.addModalPanel({ item: this.selectListView })
|
||||
this.panel = atom.workspace.addModalPanel({ item: this.selectListView });
|
||||
}
|
||||
this.selectListView.focus()
|
||||
this.selectListView.reset()
|
||||
this.selectListView.focus();
|
||||
this.selectListView.reset();
|
||||
}
|
||||
|
||||
async toggle () {
|
||||
async toggle() {
|
||||
if (this.panel != null) {
|
||||
this.cancel()
|
||||
this.cancel();
|
||||
} else if (atom.workspace.getActiveTextEditor()) {
|
||||
this.editor = atom.workspace.getActiveTextEditor()
|
||||
this.currentGrammar = this.editor.getGrammar()
|
||||
this.editor = atom.workspace.getActiveTextEditor();
|
||||
this.currentGrammar = this.editor.getGrammar();
|
||||
if (this.currentGrammar === atom.grammars.nullGrammar) {
|
||||
this.currentGrammar = this.autoDetect
|
||||
this.currentGrammar = this.autoDetect;
|
||||
}
|
||||
|
||||
const grammars = atom.grammars.getGrammars().filter(grammar => {
|
||||
return grammar !== atom.grammars.nullGrammar && grammar.name
|
||||
})
|
||||
return grammar !== atom.grammars.nullGrammar && grammar.name;
|
||||
});
|
||||
grammars.sort((a, b) => {
|
||||
if (a.scopeName === 'text.plain') {
|
||||
return -1
|
||||
return -1;
|
||||
} else if (b.scopeName === 'text.plain') {
|
||||
return 1
|
||||
return 1;
|
||||
} else if (a.name) {
|
||||
return a.name.localeCompare(b.name)
|
||||
return a.name.localeCompare(b.name);
|
||||
} else if (a.scopeName) {
|
||||
return a.scopeName.localeCompare(b.scopeName)
|
||||
return a.scopeName.localeCompare(b.scopeName);
|
||||
} else {
|
||||
return 1
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
grammars.unshift(this.autoDetect)
|
||||
await this.selectListView.update({ items: grammars })
|
||||
this.attach()
|
||||
});
|
||||
grammars.unshift(this.autoDetect);
|
||||
await this.selectListView.update({ items: grammars });
|
||||
this.attach();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,114 +1,114 @@
|
||||
const { Disposable } = require('atom')
|
||||
const { Disposable } = require('atom');
|
||||
|
||||
module.exports = class GrammarStatusView {
|
||||
constructor (statusBar) {
|
||||
this.statusBar = statusBar
|
||||
this.element = document.createElement('grammar-selector-status')
|
||||
this.element.classList.add('grammar-status', 'inline-block')
|
||||
this.grammarLink = document.createElement('a')
|
||||
this.grammarLink.classList.add('inline-block')
|
||||
this.element.appendChild(this.grammarLink)
|
||||
constructor(statusBar) {
|
||||
this.statusBar = statusBar;
|
||||
this.element = document.createElement('grammar-selector-status');
|
||||
this.element.classList.add('grammar-status', 'inline-block');
|
||||
this.grammarLink = document.createElement('a');
|
||||
this.grammarLink.classList.add('inline-block');
|
||||
this.element.appendChild(this.grammarLink);
|
||||
|
||||
this.activeItemSubscription = atom.workspace.observeActiveTextEditor(
|
||||
this.subscribeToActiveTextEditor.bind(this)
|
||||
)
|
||||
);
|
||||
|
||||
this.configSubscription = atom.config.observe(
|
||||
'grammar-selector.showOnRightSideOfStatusBar',
|
||||
this.attach.bind(this)
|
||||
)
|
||||
);
|
||||
const clickHandler = event => {
|
||||
event.preventDefault()
|
||||
event.preventDefault();
|
||||
atom.commands.dispatch(
|
||||
atom.views.getView(atom.workspace.getActiveTextEditor()),
|
||||
'grammar-selector:show'
|
||||
)
|
||||
}
|
||||
this.element.addEventListener('click', clickHandler)
|
||||
);
|
||||
};
|
||||
this.element.addEventListener('click', clickHandler);
|
||||
this.clickSubscription = new Disposable(() => {
|
||||
this.element.removeEventListener('click', clickHandler)
|
||||
})
|
||||
this.element.removeEventListener('click', clickHandler);
|
||||
});
|
||||
}
|
||||
|
||||
attach () {
|
||||
attach() {
|
||||
if (this.tile) {
|
||||
this.tile.destroy()
|
||||
this.tile.destroy();
|
||||
}
|
||||
|
||||
this.tile = atom.config.get('grammar-selector.showOnRightSideOfStatusBar')
|
||||
? this.statusBar.addRightTile({ item: this.element, priority: 10 })
|
||||
: this.statusBar.addLeftTile({ item: this.element, priority: 10 })
|
||||
: this.statusBar.addLeftTile({ item: this.element, priority: 10 });
|
||||
}
|
||||
|
||||
destroy () {
|
||||
destroy() {
|
||||
if (this.activeItemSubscription) {
|
||||
this.activeItemSubscription.dispose()
|
||||
this.activeItemSubscription.dispose();
|
||||
}
|
||||
|
||||
if (this.grammarSubscription) {
|
||||
this.grammarSubscription.dispose()
|
||||
this.grammarSubscription.dispose();
|
||||
}
|
||||
|
||||
if (this.clickSubscription) {
|
||||
this.clickSubscription.dispose()
|
||||
this.clickSubscription.dispose();
|
||||
}
|
||||
|
||||
if (this.configSubscription) {
|
||||
this.configSubscription.dispose()
|
||||
this.configSubscription.dispose();
|
||||
}
|
||||
|
||||
if (this.tile) {
|
||||
this.tile.destroy()
|
||||
this.tile.destroy();
|
||||
}
|
||||
|
||||
if (this.tooltip) {
|
||||
this.tooltip.dispose()
|
||||
this.tooltip.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
subscribeToActiveTextEditor () {
|
||||
subscribeToActiveTextEditor() {
|
||||
if (this.grammarSubscription) {
|
||||
this.grammarSubscription.dispose()
|
||||
this.grammarSubscription = null
|
||||
this.grammarSubscription.dispose();
|
||||
this.grammarSubscription = null;
|
||||
}
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
if (editor) {
|
||||
this.grammarSubscription = editor.onDidChangeGrammar(
|
||||
this.updateGrammarText.bind(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
this.updateGrammarText()
|
||||
this.updateGrammarText();
|
||||
}
|
||||
|
||||
updateGrammarText () {
|
||||
updateGrammarText() {
|
||||
atom.views.updateDocument(() => {
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
const grammar = editor ? editor.getGrammar() : null
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
const grammar = editor ? editor.getGrammar() : null;
|
||||
|
||||
if (this.tooltip) {
|
||||
this.tooltip.dispose()
|
||||
this.tooltip = null
|
||||
this.tooltip.dispose();
|
||||
this.tooltip = null;
|
||||
}
|
||||
|
||||
if (grammar) {
|
||||
let grammarName = null
|
||||
let grammarName = null;
|
||||
if (grammar === atom.grammars.nullGrammar) {
|
||||
grammarName = 'Plain Text'
|
||||
grammarName = 'Plain Text';
|
||||
} else {
|
||||
grammarName = grammar.name || grammar.scopeName
|
||||
grammarName = grammar.name || grammar.scopeName;
|
||||
}
|
||||
|
||||
this.grammarLink.textContent = grammarName
|
||||
this.grammarLink.dataset.grammar = grammarName
|
||||
this.element.style.display = ''
|
||||
this.grammarLink.textContent = grammarName;
|
||||
this.grammarLink.dataset.grammar = grammarName;
|
||||
this.element.style.display = '';
|
||||
|
||||
this.tooltip = atom.tooltips.add(this.element, {
|
||||
title: `File uses the ${grammarName} grammar`
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.element.style.display = 'none'
|
||||
this.element.style.display = 'none';
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,35 +1,35 @@
|
||||
const GrammarListView = require('./grammar-list-view')
|
||||
const GrammarStatusView = require('./grammar-status-view')
|
||||
const GrammarListView = require('./grammar-list-view');
|
||||
const GrammarStatusView = require('./grammar-status-view');
|
||||
|
||||
let commandDisposable = null
|
||||
let grammarListView = null
|
||||
let grammarStatusView = null
|
||||
let commandDisposable = null;
|
||||
let grammarListView = null;
|
||||
let grammarStatusView = null;
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
activate() {
|
||||
commandDisposable = atom.commands.add(
|
||||
'atom-text-editor',
|
||||
'grammar-selector:show',
|
||||
() => {
|
||||
if (!grammarListView) grammarListView = new GrammarListView()
|
||||
grammarListView.toggle()
|
||||
if (!grammarListView) grammarListView = new GrammarListView();
|
||||
grammarListView.toggle();
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
if (commandDisposable) commandDisposable.dispose()
|
||||
commandDisposable = null
|
||||
deactivate() {
|
||||
if (commandDisposable) commandDisposable.dispose();
|
||||
commandDisposable = null;
|
||||
|
||||
if (grammarStatusView) grammarStatusView.destroy()
|
||||
grammarStatusView = null
|
||||
if (grammarStatusView) grammarStatusView.destroy();
|
||||
grammarStatusView = null;
|
||||
|
||||
if (grammarListView) grammarListView.destroy()
|
||||
grammarListView = null
|
||||
if (grammarListView) grammarListView.destroy();
|
||||
grammarListView = null;
|
||||
},
|
||||
|
||||
consumeStatusBar (statusBar) {
|
||||
grammarStatusView = new GrammarStatusView(statusBar)
|
||||
grammarStatusView.attach()
|
||||
consumeStatusBar(statusBar) {
|
||||
grammarStatusView = new GrammarStatusView(statusBar);
|
||||
grammarStatusView.attach();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,226 +1,226 @@
|
||||
const path = require('path')
|
||||
const SelectListView = require('atom-select-list')
|
||||
const path = require('path');
|
||||
const SelectListView = require('atom-select-list');
|
||||
|
||||
describe('GrammarSelector', () => {
|
||||
let [editor, textGrammar, jsGrammar] = []
|
||||
let [editor, textGrammar, jsGrammar] = [];
|
||||
|
||||
beforeEach(async () => {
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace))
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', false)
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace));
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', false);
|
||||
|
||||
await atom.packages.activatePackage('status-bar')
|
||||
await atom.packages.activatePackage('grammar-selector')
|
||||
await atom.packages.activatePackage('language-text')
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
await atom.packages.activatePackage('status-bar');
|
||||
await atom.packages.activatePackage('grammar-selector');
|
||||
await atom.packages.activatePackage('language-text');
|
||||
await atom.packages.activatePackage('language-javascript');
|
||||
await atom.packages.activatePackage(
|
||||
path.join(__dirname, 'fixtures', 'language-with-no-name')
|
||||
)
|
||||
);
|
||||
|
||||
editor = await atom.workspace.open('sample.js')
|
||||
editor = await atom.workspace.open('sample.js');
|
||||
|
||||
textGrammar = atom.grammars.grammarForScopeName('text.plain')
|
||||
expect(textGrammar).toBeTruthy()
|
||||
jsGrammar = atom.grammars.grammarForScopeName('source.js')
|
||||
expect(jsGrammar).toBeTruthy()
|
||||
expect(editor.getGrammar()).toBe(jsGrammar)
|
||||
})
|
||||
textGrammar = atom.grammars.grammarForScopeName('text.plain');
|
||||
expect(textGrammar).toBeTruthy();
|
||||
jsGrammar = atom.grammars.grammarForScopeName('source.js');
|
||||
expect(jsGrammar).toBeTruthy();
|
||||
expect(editor.getGrammar()).toBe(jsGrammar);
|
||||
});
|
||||
|
||||
describe('when grammar-selector:show is triggered', () =>
|
||||
it('displays a list of all the available grammars', async () => {
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem().element
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem().element;
|
||||
// TODO: Remove once Atom 1.23 reaches stable
|
||||
if (parseFloat(atom.getVersion()) >= 1.23) {
|
||||
// Do not take into account the two JS regex grammars or language-with-no-name
|
||||
expect(grammarView.querySelectorAll('li').length).toBe(
|
||||
atom.grammars.grammars.length - 3
|
||||
)
|
||||
);
|
||||
} else {
|
||||
expect(grammarView.querySelectorAll('li').length).toBe(
|
||||
atom.grammars.grammars.length - 1
|
||||
)
|
||||
);
|
||||
}
|
||||
expect(grammarView.querySelectorAll('li')[0].textContent).toBe(
|
||||
'Auto Detect'
|
||||
)
|
||||
expect(grammarView.textContent.includes('source.a')).toBe(false)
|
||||
);
|
||||
expect(grammarView.textContent.includes('source.a')).toBe(false);
|
||||
grammarView
|
||||
.querySelectorAll('li')
|
||||
.forEach(li =>
|
||||
expect(li.textContent).not.toBe(atom.grammars.nullGrammar.name)
|
||||
)
|
||||
}))
|
||||
);
|
||||
}));
|
||||
|
||||
describe('when a grammar is selected', () =>
|
||||
it('sets the new grammar on the editor', async () => {
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem()
|
||||
grammarView.props.didConfirmSelection(textGrammar)
|
||||
expect(editor.getGrammar()).toBe(textGrammar)
|
||||
}))
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem();
|
||||
grammarView.props.didConfirmSelection(textGrammar);
|
||||
expect(editor.getGrammar()).toBe(textGrammar);
|
||||
}));
|
||||
|
||||
describe('when auto-detect is selected', () =>
|
||||
it('restores the auto-detected grammar on the editor', async () => {
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
let grammarView = atom.workspace.getModalPanels()[0].getItem()
|
||||
grammarView.props.didConfirmSelection(textGrammar)
|
||||
expect(editor.getGrammar()).toBe(textGrammar)
|
||||
let grammarView = atom.workspace.getModalPanels()[0].getItem();
|
||||
grammarView.props.didConfirmSelection(textGrammar);
|
||||
expect(editor.getGrammar()).toBe(textGrammar);
|
||||
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
grammarView = atom.workspace.getModalPanels()[0].getItem()
|
||||
grammarView.props.didConfirmSelection(grammarView.items[0])
|
||||
expect(editor.getGrammar()).toBe(jsGrammar)
|
||||
}))
|
||||
grammarView = atom.workspace.getModalPanels()[0].getItem();
|
||||
grammarView.props.didConfirmSelection(grammarView.items[0]);
|
||||
expect(editor.getGrammar()).toBe(jsGrammar);
|
||||
}));
|
||||
|
||||
describe("when the editor's current grammar is the null grammar", () =>
|
||||
it('displays Auto Detect as the selected grammar', async () => {
|
||||
editor.setGrammar(atom.grammars.nullGrammar)
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
editor.setGrammar(atom.grammars.nullGrammar);
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem().element
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem().element;
|
||||
expect(grammarView.querySelector('li.active').textContent).toBe(
|
||||
'Auto Detect'
|
||||
)
|
||||
}))
|
||||
);
|
||||
}));
|
||||
|
||||
describe('when editor is untitled', () =>
|
||||
it('sets the new grammar on the editor', async () => {
|
||||
editor = await atom.workspace.open()
|
||||
expect(editor.getGrammar()).not.toBe(jsGrammar)
|
||||
editor = await atom.workspace.open();
|
||||
expect(editor.getGrammar()).not.toBe(jsGrammar);
|
||||
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show')
|
||||
await SelectListView.getScheduler().getNextUpdatePromise()
|
||||
atom.commands.dispatch(editor.getElement(), 'grammar-selector:show');
|
||||
await SelectListView.getScheduler().getNextUpdatePromise();
|
||||
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem()
|
||||
grammarView.props.didConfirmSelection(jsGrammar)
|
||||
expect(editor.getGrammar()).toBe(jsGrammar)
|
||||
}))
|
||||
const grammarView = atom.workspace.getModalPanels()[0].getItem();
|
||||
grammarView.props.didConfirmSelection(jsGrammar);
|
||||
expect(editor.getGrammar()).toBe(jsGrammar);
|
||||
}));
|
||||
|
||||
describe('Status bar grammar label', () => {
|
||||
let [grammarStatus, grammarTile, statusBar] = []
|
||||
let [grammarStatus, grammarTile, statusBar] = [];
|
||||
|
||||
beforeEach(async () => {
|
||||
statusBar = document.querySelector('status-bar')
|
||||
;[grammarTile] = statusBar.getLeftTiles().slice(-1)
|
||||
grammarStatus = grammarTile.getItem()
|
||||
statusBar = document.querySelector('status-bar');
|
||||
[grammarTile] = statusBar.getLeftTiles().slice(-1);
|
||||
grammarStatus = grammarTile.getItem();
|
||||
|
||||
// Wait for status bar service hook to fire
|
||||
while (!grammarStatus || !grammarStatus.textContent) {
|
||||
await atom.views.getNextUpdatePromise()
|
||||
grammarStatus = document.querySelector('.grammar-status')
|
||||
await atom.views.getNextUpdatePromise();
|
||||
grammarStatus = document.querySelector('.grammar-status');
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('displays the name of the current grammar', () => {
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('JavaScript')
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('JavaScript');
|
||||
expect(getTooltipText(grammarStatus)).toBe(
|
||||
'File uses the JavaScript grammar'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('displays Plain Text when the current grammar is the null grammar', async () => {
|
||||
editor.setGrammar(atom.grammars.nullGrammar)
|
||||
await atom.views.getNextUpdatePromise()
|
||||
editor.setGrammar(atom.grammars.nullGrammar);
|
||||
await atom.views.getNextUpdatePromise();
|
||||
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text')
|
||||
expect(grammarStatus).toBeVisible()
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text');
|
||||
expect(grammarStatus).toBeVisible();
|
||||
expect(getTooltipText(grammarStatus)).toBe(
|
||||
'File uses the Plain Text grammar'
|
||||
)
|
||||
);
|
||||
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'))
|
||||
await atom.views.getNextUpdatePromise()
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.js'));
|
||||
await atom.views.getNextUpdatePromise();
|
||||
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('JavaScript')
|
||||
expect(grammarStatus).toBeVisible()
|
||||
})
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('JavaScript');
|
||||
expect(grammarStatus).toBeVisible();
|
||||
});
|
||||
|
||||
it('hides the label when the current grammar is null', async () => {
|
||||
jasmine.attachToDOM(editor.getElement())
|
||||
spyOn(editor, 'getGrammar').andReturn(null)
|
||||
editor.setGrammar(atom.grammars.nullGrammar)
|
||||
await atom.views.getNextUpdatePromise()
|
||||
expect(grammarStatus.offsetHeight).toBe(0)
|
||||
})
|
||||
jasmine.attachToDOM(editor.getElement());
|
||||
spyOn(editor, 'getGrammar').andReturn(null);
|
||||
editor.setGrammar(atom.grammars.nullGrammar);
|
||||
await atom.views.getNextUpdatePromise();
|
||||
expect(grammarStatus.offsetHeight).toBe(0);
|
||||
});
|
||||
|
||||
describe('when the grammar-selector.showOnRightSideOfStatusBar setting changes', () =>
|
||||
it('moves the item to the preferred side of the status bar', () => {
|
||||
expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain(
|
||||
grammarStatus
|
||||
)
|
||||
);
|
||||
expect(
|
||||
statusBar.getRightTiles().map(tile => tile.getItem())
|
||||
).not.toContain(grammarStatus)
|
||||
).not.toContain(grammarStatus);
|
||||
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', true)
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', true);
|
||||
|
||||
expect(
|
||||
statusBar.getLeftTiles().map(tile => tile.getItem())
|
||||
).not.toContain(grammarStatus)
|
||||
).not.toContain(grammarStatus);
|
||||
expect(statusBar.getRightTiles().map(tile => tile.getItem())).toContain(
|
||||
grammarStatus
|
||||
)
|
||||
);
|
||||
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', false)
|
||||
atom.config.set('grammar-selector.showOnRightSideOfStatusBar', false);
|
||||
|
||||
expect(statusBar.getLeftTiles().map(tile => tile.getItem())).toContain(
|
||||
grammarStatus
|
||||
)
|
||||
);
|
||||
expect(
|
||||
statusBar.getRightTiles().map(tile => tile.getItem())
|
||||
).not.toContain(grammarStatus)
|
||||
}))
|
||||
).not.toContain(grammarStatus);
|
||||
}));
|
||||
|
||||
describe("when the editor's grammar changes", () =>
|
||||
it('displays the new grammar of the editor', async () => {
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('text.plain'))
|
||||
await atom.views.getNextUpdatePromise()
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('text.plain'));
|
||||
await atom.views.getNextUpdatePromise();
|
||||
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text')
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('Plain Text');
|
||||
expect(getTooltipText(grammarStatus)).toBe(
|
||||
'File uses the Plain Text grammar'
|
||||
)
|
||||
);
|
||||
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.a'))
|
||||
await atom.views.getNextUpdatePromise()
|
||||
editor.setGrammar(atom.grammars.grammarForScopeName('source.a'));
|
||||
await atom.views.getNextUpdatePromise();
|
||||
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('source.a')
|
||||
expect(grammarStatus.querySelector('a').textContent).toBe('source.a');
|
||||
expect(getTooltipText(grammarStatus)).toBe(
|
||||
'File uses the source.a grammar'
|
||||
)
|
||||
}))
|
||||
);
|
||||
}));
|
||||
|
||||
describe('when clicked', () =>
|
||||
it('shows the grammar selector modal', () => {
|
||||
const eventHandler = jasmine.createSpy('eventHandler')
|
||||
const eventHandler = jasmine.createSpy('eventHandler');
|
||||
atom.commands.add(
|
||||
editor.getElement(),
|
||||
'grammar-selector:show',
|
||||
eventHandler
|
||||
)
|
||||
grammarStatus.click()
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
}))
|
||||
);
|
||||
grammarStatus.click();
|
||||
expect(eventHandler).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
describe('when the package is deactivated', () =>
|
||||
it('removes the view', () => {
|
||||
spyOn(grammarTile, 'destroy')
|
||||
atom.packages.deactivatePackage('grammar-selector')
|
||||
expect(grammarTile.destroy).toHaveBeenCalled()
|
||||
}))
|
||||
})
|
||||
})
|
||||
spyOn(grammarTile, 'destroy');
|
||||
atom.packages.deactivatePackage('grammar-selector');
|
||||
expect(grammarTile.destroy).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
function getTooltipText (element) {
|
||||
const [tooltip] = atom.tooltips.findTooltips(element)
|
||||
return tooltip.getTitle()
|
||||
function getTooltipText(element) {
|
||||
const [tooltip] = atom.tooltips.findTooltips(element);
|
||||
return tooltip.getTitle();
|
||||
}
|
||||
|
@ -1,239 +1,239 @@
|
||||
/** @babel */
|
||||
/** @jsx etch.dom */
|
||||
|
||||
import etch from 'etch'
|
||||
import etch from 'etch';
|
||||
|
||||
import VIEW_URI from './view-uri'
|
||||
const REBUILDING = 'rebuilding'
|
||||
const REBUILD_FAILED = 'rebuild-failed'
|
||||
const REBUILD_SUCCEEDED = 'rebuild-succeeded'
|
||||
import VIEW_URI from './view-uri';
|
||||
const REBUILDING = 'rebuilding';
|
||||
const REBUILD_FAILED = 'rebuild-failed';
|
||||
const REBUILD_SUCCEEDED = 'rebuild-succeeded';
|
||||
|
||||
export default class IncompatiblePackagesComponent {
|
||||
constructor (packageManager) {
|
||||
this.rebuildStatuses = new Map()
|
||||
this.rebuildFailureOutputs = new Map()
|
||||
this.rebuildInProgress = false
|
||||
this.rebuiltPackageCount = 0
|
||||
this.packageManager = packageManager
|
||||
this.loaded = false
|
||||
etch.initialize(this)
|
||||
constructor(packageManager) {
|
||||
this.rebuildStatuses = new Map();
|
||||
this.rebuildFailureOutputs = new Map();
|
||||
this.rebuildInProgress = false;
|
||||
this.rebuiltPackageCount = 0;
|
||||
this.packageManager = packageManager;
|
||||
this.loaded = false;
|
||||
etch.initialize(this);
|
||||
|
||||
if (this.packageManager.getActivePackages().length > 0) {
|
||||
this.populateIncompatiblePackages()
|
||||
this.populateIncompatiblePackages();
|
||||
} else {
|
||||
global.setImmediate(this.populateIncompatiblePackages.bind(this))
|
||||
global.setImmediate(this.populateIncompatiblePackages.bind(this));
|
||||
}
|
||||
|
||||
this.element.addEventListener('click', event => {
|
||||
if (event.target === this.refs.rebuildButton) {
|
||||
this.rebuildIncompatiblePackages()
|
||||
this.rebuildIncompatiblePackages();
|
||||
} else if (event.target === this.refs.reloadButton) {
|
||||
atom.reload()
|
||||
atom.reload();
|
||||
} else if (event.target.classList.contains('view-settings')) {
|
||||
atom.workspace.open(
|
||||
`atom://config/packages/${event.target.package.name}`
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
update () {}
|
||||
update() {}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
if (!this.loaded) {
|
||||
return <div className='incompatible-packages padded'>Loading...</div>
|
||||
return <div className="incompatible-packages padded">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='incompatible-packages padded native-key-bindings'
|
||||
tabIndex='-1'
|
||||
className="incompatible-packages padded native-key-bindings"
|
||||
tabIndex="-1"
|
||||
>
|
||||
{this.renderHeading()}
|
||||
{this.renderIncompatiblePackageList()}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderHeading () {
|
||||
renderHeading() {
|
||||
if (this.incompatiblePackages.length > 0) {
|
||||
if (this.rebuiltPackageCount > 0) {
|
||||
let alertClass =
|
||||
this.rebuiltPackageCount === this.incompatiblePackages.length
|
||||
? 'alert-success icon-check'
|
||||
: 'alert-warning icon-bug'
|
||||
: 'alert-warning icon-bug';
|
||||
|
||||
return (
|
||||
<div className={'alert icon ' + alertClass}>
|
||||
{this.rebuiltPackageCount} of {this.incompatiblePackages.length}{' '}
|
||||
packages were rebuilt successfully. Reload Atom to activate them.
|
||||
<button ref='reloadButton' className='btn pull-right'>
|
||||
<button ref="reloadButton" className="btn pull-right">
|
||||
Reload Atom
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className='alert alert-danger icon icon-bug'>
|
||||
<div className="alert alert-danger icon icon-bug">
|
||||
Some installed packages could not be loaded because they contain
|
||||
native modules that were compiled for an earlier version of Atom.
|
||||
<button
|
||||
ref='rebuildButton'
|
||||
className='btn pull-right'
|
||||
ref="rebuildButton"
|
||||
className="btn pull-right"
|
||||
disabled={this.rebuildInProgress}
|
||||
>
|
||||
Rebuild Packages
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<div className='alert alert-success icon icon-check'>
|
||||
<div className="alert alert-success icon icon-check">
|
||||
None of your packages contain incompatible native modules.
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderIncompatiblePackageList () {
|
||||
renderIncompatiblePackageList() {
|
||||
return (
|
||||
<div>
|
||||
{this.incompatiblePackages.map(
|
||||
this.renderIncompatiblePackage.bind(this)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderIncompatiblePackage (pack) {
|
||||
let rebuildStatus = this.rebuildStatuses.get(pack)
|
||||
renderIncompatiblePackage(pack) {
|
||||
let rebuildStatus = this.rebuildStatuses.get(pack);
|
||||
|
||||
return (
|
||||
<div className={'incompatible-package'}>
|
||||
{this.renderRebuildStatusIndicator(rebuildStatus)}
|
||||
<button
|
||||
className='btn view-settings icon icon-gear pull-right'
|
||||
className="btn view-settings icon icon-gear pull-right"
|
||||
package={pack}
|
||||
>
|
||||
Package Settings
|
||||
</button>
|
||||
<h4 className='heading'>
|
||||
<h4 className="heading">
|
||||
{pack.name} {pack.metadata.version}
|
||||
</h4>
|
||||
{rebuildStatus
|
||||
? this.renderRebuildOutput(pack)
|
||||
: this.renderIncompatibleModules(pack)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderRebuildStatusIndicator (rebuildStatus) {
|
||||
renderRebuildStatusIndicator(rebuildStatus) {
|
||||
if (rebuildStatus === REBUILDING) {
|
||||
return (
|
||||
<div className='badge badge-info pull-right icon icon-gear'>
|
||||
<div className="badge badge-info pull-right icon icon-gear">
|
||||
Rebuilding
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else if (rebuildStatus === REBUILD_SUCCEEDED) {
|
||||
return (
|
||||
<div className='badge badge-success pull-right icon icon-check'>
|
||||
<div className="badge badge-success pull-right icon icon-check">
|
||||
Rebuild Succeeded
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else if (rebuildStatus === REBUILD_FAILED) {
|
||||
return (
|
||||
<div className='badge badge-error pull-right icon icon-x'>
|
||||
<div className="badge badge-error pull-right icon icon-x">
|
||||
Rebuild Failed
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
renderRebuildOutput (pack) {
|
||||
renderRebuildOutput(pack) {
|
||||
if (this.rebuildStatuses.get(pack) === REBUILD_FAILED) {
|
||||
return <pre>{this.rebuildFailureOutputs.get(pack)}</pre>
|
||||
return <pre>{this.rebuildFailureOutputs.get(pack)}</pre>;
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
renderIncompatibleModules (pack) {
|
||||
renderIncompatibleModules(pack) {
|
||||
return (
|
||||
<ul>
|
||||
{pack.incompatibleModules.map(nativeModule => (
|
||||
<li>
|
||||
<div className='icon icon-file-binary'>
|
||||
<div className="icon icon-file-binary">
|
||||
{nativeModule.name}@{nativeModule.version || 'unknown'} –{' '}
|
||||
<span className='text-warning'>{nativeModule.error}</span>
|
||||
<span className="text-warning">{nativeModule.error}</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
populateIncompatiblePackages () {
|
||||
populateIncompatiblePackages() {
|
||||
this.incompatiblePackages = this.packageManager
|
||||
.getLoadedPackages()
|
||||
.filter(pack => !pack.isCompatible())
|
||||
.filter(pack => !pack.isCompatible());
|
||||
|
||||
for (let pack of this.incompatiblePackages) {
|
||||
let buildFailureOutput = pack.getBuildFailureOutput()
|
||||
let buildFailureOutput = pack.getBuildFailureOutput();
|
||||
if (buildFailureOutput) {
|
||||
this.setPackageStatus(pack, REBUILD_FAILED)
|
||||
this.setRebuildFailureOutput(pack, buildFailureOutput)
|
||||
this.setPackageStatus(pack, REBUILD_FAILED);
|
||||
this.setRebuildFailureOutput(pack, buildFailureOutput);
|
||||
}
|
||||
}
|
||||
|
||||
this.loaded = true
|
||||
etch.update(this)
|
||||
this.loaded = true;
|
||||
etch.update(this);
|
||||
}
|
||||
|
||||
async rebuildIncompatiblePackages () {
|
||||
this.rebuildInProgress = true
|
||||
let rebuiltPackageCount = 0
|
||||
async rebuildIncompatiblePackages() {
|
||||
this.rebuildInProgress = true;
|
||||
let rebuiltPackageCount = 0;
|
||||
for (let pack of this.incompatiblePackages) {
|
||||
this.setPackageStatus(pack, REBUILDING)
|
||||
let { code, stderr } = await pack.rebuild()
|
||||
this.setPackageStatus(pack, REBUILDING);
|
||||
let { code, stderr } = await pack.rebuild();
|
||||
if (code === 0) {
|
||||
this.setPackageStatus(pack, REBUILD_SUCCEEDED)
|
||||
rebuiltPackageCount++
|
||||
this.setPackageStatus(pack, REBUILD_SUCCEEDED);
|
||||
rebuiltPackageCount++;
|
||||
} else {
|
||||
this.setRebuildFailureOutput(pack, stderr)
|
||||
this.setPackageStatus(pack, REBUILD_FAILED)
|
||||
this.setRebuildFailureOutput(pack, stderr);
|
||||
this.setPackageStatus(pack, REBUILD_FAILED);
|
||||
}
|
||||
}
|
||||
this.rebuildInProgress = false
|
||||
this.rebuiltPackageCount = rebuiltPackageCount
|
||||
etch.update(this)
|
||||
this.rebuildInProgress = false;
|
||||
this.rebuiltPackageCount = rebuiltPackageCount;
|
||||
etch.update(this);
|
||||
}
|
||||
|
||||
setPackageStatus (pack, status) {
|
||||
this.rebuildStatuses.set(pack, status)
|
||||
etch.update(this)
|
||||
setPackageStatus(pack, status) {
|
||||
this.rebuildStatuses.set(pack, status);
|
||||
etch.update(this);
|
||||
}
|
||||
|
||||
setRebuildFailureOutput (pack, output) {
|
||||
this.rebuildFailureOutputs.set(pack, output)
|
||||
etch.update(this)
|
||||
setRebuildFailureOutput(pack, output) {
|
||||
this.rebuildFailureOutputs.set(pack, output);
|
||||
etch.update(this);
|
||||
}
|
||||
|
||||
getTitle () {
|
||||
return 'Incompatible Packages'
|
||||
getTitle() {
|
||||
return 'Incompatible Packages';
|
||||
}
|
||||
|
||||
getURI () {
|
||||
return VIEW_URI
|
||||
getURI() {
|
||||
return VIEW_URI;
|
||||
}
|
||||
|
||||
getIconName () {
|
||||
return 'package'
|
||||
getIconName() {
|
||||
return 'package';
|
||||
}
|
||||
|
||||
serialize () {
|
||||
return { deserializer: 'IncompatiblePackagesComponent' }
|
||||
serialize() {
|
||||
return { deserializer: 'IncompatiblePackagesComponent' };
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +1,56 @@
|
||||
/** @babel */
|
||||
|
||||
import { Disposable, CompositeDisposable } from 'atom'
|
||||
import VIEW_URI from './view-uri'
|
||||
import { Disposable, CompositeDisposable } from 'atom';
|
||||
import VIEW_URI from './view-uri';
|
||||
|
||||
let disposables = null
|
||||
let disposables = null;
|
||||
|
||||
export function activate () {
|
||||
disposables = new CompositeDisposable()
|
||||
export function activate() {
|
||||
disposables = new CompositeDisposable();
|
||||
|
||||
disposables.add(
|
||||
atom.workspace.addOpener(uri => {
|
||||
if (uri === VIEW_URI) {
|
||||
return deserializeIncompatiblePackagesComponent()
|
||||
return deserializeIncompatiblePackagesComponent();
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
disposables.add(
|
||||
atom.commands.add('atom-workspace', {
|
||||
'incompatible-packages:view': () => {
|
||||
atom.workspace.open(VIEW_URI)
|
||||
atom.workspace.open(VIEW_URI);
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate () {
|
||||
disposables.dispose()
|
||||
export function deactivate() {
|
||||
disposables.dispose();
|
||||
}
|
||||
|
||||
export function consumeStatusBar (statusBar) {
|
||||
let incompatibleCount = 0
|
||||
export function consumeStatusBar(statusBar) {
|
||||
let incompatibleCount = 0;
|
||||
for (let pack of atom.packages.getLoadedPackages()) {
|
||||
if (!pack.isCompatible()) incompatibleCount++
|
||||
if (!pack.isCompatible()) incompatibleCount++;
|
||||
}
|
||||
|
||||
if (incompatibleCount > 0) {
|
||||
let icon = createIcon(incompatibleCount)
|
||||
let tile = statusBar.addRightTile({ item: icon, priority: 200 })
|
||||
let icon = createIcon(incompatibleCount);
|
||||
let tile = statusBar.addRightTile({ item: icon, priority: 200 });
|
||||
icon.element.addEventListener('click', () => {
|
||||
atom.commands.dispatch(icon.element, 'incompatible-packages:view')
|
||||
})
|
||||
disposables.add(new Disposable(() => tile.destroy()))
|
||||
atom.commands.dispatch(icon.element, 'incompatible-packages:view');
|
||||
});
|
||||
disposables.add(new Disposable(() => tile.destroy()));
|
||||
}
|
||||
}
|
||||
|
||||
export function deserializeIncompatiblePackagesComponent () {
|
||||
const IncompatiblePackagesComponent = require('./incompatible-packages-component')
|
||||
return new IncompatiblePackagesComponent(atom.packages)
|
||||
export function deserializeIncompatiblePackagesComponent() {
|
||||
const IncompatiblePackagesComponent = require('./incompatible-packages-component');
|
||||
return new IncompatiblePackagesComponent(atom.packages);
|
||||
}
|
||||
|
||||
function createIcon (count) {
|
||||
const StatusIconComponent = require('./status-icon-component')
|
||||
return new StatusIconComponent({ count })
|
||||
function createIcon(count) {
|
||||
const StatusIconComponent = require('./status-icon-component');
|
||||
return new StatusIconComponent({ count });
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
/** @babel */
|
||||
/** @jsx etch.dom */
|
||||
|
||||
import etch from 'etch'
|
||||
import etch from 'etch';
|
||||
|
||||
export default class StatusIconComponent {
|
||||
constructor ({ count }) {
|
||||
this.count = count
|
||||
etch.initialize(this)
|
||||
constructor({ count }) {
|
||||
this.count = count;
|
||||
etch.initialize(this);
|
||||
}
|
||||
|
||||
update () {}
|
||||
update() {}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
return (
|
||||
<div className='incompatible-packages-status inline-block text text-error'>
|
||||
<span className='icon icon-bug' />
|
||||
<span className='incompatible-packages-count'>{this.count}</span>
|
||||
<div className="incompatible-packages-status inline-block text text-error">
|
||||
<span className="icon icon-bug" />
|
||||
<span className="incompatible-packages-count">{this.count}</span>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
/** @babel */
|
||||
|
||||
export default 'atom://incompatible-packages'
|
||||
export default 'atom://incompatible-packages';
|
||||
|
@ -1,25 +1,25 @@
|
||||
/** @babel */
|
||||
|
||||
import etch from 'etch'
|
||||
import IncompatiblePackagesComponent from '../lib/incompatible-packages-component'
|
||||
import etch from 'etch';
|
||||
import IncompatiblePackagesComponent from '../lib/incompatible-packages-component';
|
||||
|
||||
describe('IncompatiblePackagesComponent', () => {
|
||||
let packages, etchScheduler
|
||||
let packages, etchScheduler;
|
||||
|
||||
beforeEach(() => {
|
||||
etchScheduler = etch.getScheduler()
|
||||
etchScheduler = etch.getScheduler();
|
||||
|
||||
packages = [
|
||||
{
|
||||
name: 'incompatible-1',
|
||||
isCompatible () {
|
||||
return false
|
||||
isCompatible() {
|
||||
return false;
|
||||
},
|
||||
rebuild: function () {
|
||||
return new Promise(resolve => (this.resolveRebuild = resolve))
|
||||
rebuild: function() {
|
||||
return new Promise(resolve => (this.resolveRebuild = resolve));
|
||||
},
|
||||
getBuildFailureOutput () {
|
||||
return null
|
||||
getBuildFailureOutput() {
|
||||
return null;
|
||||
},
|
||||
path: '/Users/joe/.atom/packages/incompatible-1',
|
||||
metadata: {
|
||||
@ -33,14 +33,14 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
},
|
||||
{
|
||||
name: 'incompatible-2',
|
||||
isCompatible () {
|
||||
return false
|
||||
isCompatible() {
|
||||
return false;
|
||||
},
|
||||
rebuild () {
|
||||
return new Promise(resolve => (this.resolveRebuild = resolve))
|
||||
rebuild() {
|
||||
return new Promise(resolve => (this.resolveRebuild = resolve));
|
||||
},
|
||||
getBuildFailureOutput () {
|
||||
return null
|
||||
getBuildFailureOutput() {
|
||||
return null;
|
||||
},
|
||||
path: '/Users/joe/.atom/packages/incompatible-2',
|
||||
metadata: {
|
||||
@ -53,14 +53,14 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
},
|
||||
{
|
||||
name: 'compatible',
|
||||
isCompatible () {
|
||||
return true
|
||||
isCompatible() {
|
||||
return true;
|
||||
},
|
||||
rebuild () {
|
||||
throw new Error('Should not rebuild a compatible package')
|
||||
rebuild() {
|
||||
throw new Error('Should not rebuild a compatible package');
|
||||
},
|
||||
getBuildFailureOutput () {
|
||||
return null
|
||||
getBuildFailureOutput() {
|
||||
return null;
|
||||
},
|
||||
path: '/Users/joe/.atom/packages/b',
|
||||
metadata: {
|
||||
@ -69,8 +69,8 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
},
|
||||
incompatibleModules: []
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
});
|
||||
|
||||
describe('when packages have not finished loading', () => {
|
||||
it('delays rendering incompatible packages until the end of the tick', () => {
|
||||
@ -78,69 +78,71 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => [],
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
});
|
||||
let { element } = component;
|
||||
|
||||
expect(
|
||||
element.querySelectorAll('.incompatible-package').length
|
||||
).toEqual(0)
|
||||
).toEqual(0);
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
|
||||
expect(
|
||||
element.querySelectorAll('.incompatible-package').length
|
||||
).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no incompatible packages', () => {
|
||||
it('does not render incompatible packages or the rebuild button', () => {
|
||||
waitsForPromise(async () => {
|
||||
expect(packages[2].isCompatible()).toBe(true)
|
||||
let compatiblePackages = [packages[2]]
|
||||
expect(packages[2].isCompatible()).toBe(true);
|
||||
let compatiblePackages = [packages[2]];
|
||||
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => compatiblePackages,
|
||||
getLoadedPackages: () => compatiblePackages
|
||||
})
|
||||
let { element } = component
|
||||
});
|
||||
let { element } = component;
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
|
||||
expect(element.querySelectorAll('.incompatible-package').length).toBe(0)
|
||||
expect(element.querySelector('button')).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(element.querySelectorAll('.incompatible-package').length).toBe(
|
||||
0
|
||||
);
|
||||
expect(element.querySelector('button')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when some packages previously failed to rebuild', () => {
|
||||
it('renders them with failed build status and error output', () => {
|
||||
waitsForPromise(async () => {
|
||||
packages[1].getBuildFailureOutput = function () {
|
||||
return 'The build failed'
|
||||
}
|
||||
packages[1].getBuildFailureOutput = function() {
|
||||
return 'The build failed';
|
||||
};
|
||||
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => packages,
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
});
|
||||
let { element } = component;
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
let packageElement = element.querySelector(
|
||||
'.incompatible-package:nth-child(2)'
|
||||
)
|
||||
);
|
||||
|
||||
expect(packageElement.querySelector('.badge').textContent).toBe(
|
||||
'Rebuild Failed'
|
||||
)
|
||||
);
|
||||
expect(packageElement.querySelector('pre').textContent).toBe(
|
||||
'The build failed'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are incompatible packages', () => {
|
||||
it('renders incompatible packages and the rebuild button', () => {
|
||||
@ -148,17 +150,17 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => packages,
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
});
|
||||
let { element } = component;
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
|
||||
expect(
|
||||
element.querySelectorAll('.incompatible-package').length
|
||||
).toEqual(2)
|
||||
expect(element.querySelector('button')).not.toBeNull()
|
||||
})
|
||||
})
|
||||
).toEqual(2);
|
||||
expect(element.querySelector('button')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the "Rebuild All" button is clicked', () => {
|
||||
it("rebuilds every incompatible package, updating each package's view with status", () => {
|
||||
@ -166,94 +168,94 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => packages,
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
jasmine.attachToDOM(element)
|
||||
});
|
||||
let { element } = component;
|
||||
jasmine.attachToDOM(element);
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
|
||||
component.refs.rebuildButton.dispatchEvent(
|
||||
new CustomEvent('click', { bubbles: true })
|
||||
)
|
||||
await etchScheduler.getNextUpdatePromise() // view update
|
||||
);
|
||||
await etchScheduler.getNextUpdatePromise(); // view update
|
||||
|
||||
expect(component.refs.rebuildButton.disabled).toBe(true)
|
||||
expect(component.refs.rebuildButton.disabled).toBe(true);
|
||||
|
||||
expect(packages[0].resolveRebuild).toBeDefined()
|
||||
expect(packages[0].resolveRebuild).toBeDefined();
|
||||
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(1) .badge')
|
||||
.textContent
|
||||
).toBe('Rebuilding')
|
||||
).toBe('Rebuilding');
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(2) .badge')
|
||||
).toBeNull()
|
||||
).toBeNull();
|
||||
|
||||
packages[0].resolveRebuild({ code: 0 }) // simulate rebuild success
|
||||
await etchScheduler.getNextUpdatePromise() // view update
|
||||
packages[0].resolveRebuild({ code: 0 }); // simulate rebuild success
|
||||
await etchScheduler.getNextUpdatePromise(); // view update
|
||||
|
||||
expect(packages[1].resolveRebuild).toBeDefined()
|
||||
expect(packages[1].resolveRebuild).toBeDefined();
|
||||
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(1) .badge')
|
||||
.textContent
|
||||
).toBe('Rebuild Succeeded')
|
||||
).toBe('Rebuild Succeeded');
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(2) .badge')
|
||||
.textContent
|
||||
).toBe('Rebuilding')
|
||||
).toBe('Rebuilding');
|
||||
|
||||
packages[1].resolveRebuild({
|
||||
code: 12,
|
||||
stderr: 'This is an error from the test!'
|
||||
}) // simulate rebuild failure
|
||||
await etchScheduler.getNextUpdatePromise() // view update
|
||||
}); // simulate rebuild failure
|
||||
await etchScheduler.getNextUpdatePromise(); // view update
|
||||
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(1) .badge')
|
||||
.textContent
|
||||
).toBe('Rebuild Succeeded')
|
||||
).toBe('Rebuild Succeeded');
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(2) .badge')
|
||||
.textContent
|
||||
).toBe('Rebuild Failed')
|
||||
).toBe('Rebuild Failed');
|
||||
expect(
|
||||
element.querySelector('.incompatible-package:nth-child(2) pre')
|
||||
.textContent
|
||||
).toBe('This is an error from the test!')
|
||||
})
|
||||
})
|
||||
).toBe('This is an error from the test!');
|
||||
});
|
||||
});
|
||||
|
||||
it('displays a prompt to reload Atom when the packages finish rebuilding', () => {
|
||||
waitsForPromise(async () => {
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => packages,
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
jasmine.attachToDOM(element)
|
||||
await etchScheduler.getNextUpdatePromise() // view update
|
||||
});
|
||||
let { element } = component;
|
||||
jasmine.attachToDOM(element);
|
||||
await etchScheduler.getNextUpdatePromise(); // view update
|
||||
|
||||
component.refs.rebuildButton.dispatchEvent(
|
||||
new CustomEvent('click', { bubbles: true })
|
||||
)
|
||||
expect(packages[0].resolveRebuild({ code: 0 }))
|
||||
await new Promise(global.setImmediate)
|
||||
expect(packages[1].resolveRebuild({ code: 0 }))
|
||||
);
|
||||
expect(packages[0].resolveRebuild({ code: 0 }));
|
||||
await new Promise(global.setImmediate);
|
||||
expect(packages[1].resolveRebuild({ code: 0 }));
|
||||
|
||||
await etchScheduler.getNextUpdatePromise() // view update
|
||||
await etchScheduler.getNextUpdatePromise(); // view update
|
||||
|
||||
expect(component.refs.reloadButton).toBeDefined()
|
||||
expect(element.querySelector('.alert').textContent).toMatch(/2 of 2/)
|
||||
expect(component.refs.reloadButton).toBeDefined();
|
||||
expect(element.querySelector('.alert').textContent).toMatch(/2 of 2/);
|
||||
|
||||
spyOn(atom, 'reload')
|
||||
spyOn(atom, 'reload');
|
||||
component.refs.reloadButton.dispatchEvent(
|
||||
new CustomEvent('click', { bubbles: true })
|
||||
)
|
||||
expect(atom.reload).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(atom.reload).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the "Package Settings" button is clicked', () => {
|
||||
it('opens the settings panel for the package', () => {
|
||||
@ -261,21 +263,21 @@ describe('IncompatiblePackagesComponent', () => {
|
||||
let component = new IncompatiblePackagesComponent({
|
||||
getActivePackages: () => packages,
|
||||
getLoadedPackages: () => packages
|
||||
})
|
||||
let { element } = component
|
||||
jasmine.attachToDOM(element)
|
||||
});
|
||||
let { element } = component;
|
||||
jasmine.attachToDOM(element);
|
||||
|
||||
await etchScheduler.getNextUpdatePromise()
|
||||
await etchScheduler.getNextUpdatePromise();
|
||||
|
||||
spyOn(atom.workspace, 'open')
|
||||
spyOn(atom.workspace, 'open');
|
||||
element
|
||||
.querySelector('.incompatible-package:nth-child(2) button')
|
||||
.dispatchEvent(new CustomEvent('click', { bubbles: true }))
|
||||
.dispatchEvent(new CustomEvent('click', { bubbles: true }));
|
||||
expect(atom.workspace.open).toHaveBeenCalledWith(
|
||||
'atom://config/packages/incompatible-2'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,81 +1,83 @@
|
||||
/** @babel */
|
||||
|
||||
import path from 'path'
|
||||
import IncompatiblePackagesComponent from '../lib/incompatible-packages-component'
|
||||
import StatusIconComponent from '../lib/status-icon-component'
|
||||
import path from 'path';
|
||||
import IncompatiblePackagesComponent from '../lib/incompatible-packages-component';
|
||||
import StatusIconComponent from '../lib/status-icon-component';
|
||||
|
||||
// This exists only so that CI passes on both Atom 1.6 and Atom 1.8+.
|
||||
function findStatusBar () {
|
||||
function findStatusBar() {
|
||||
if (typeof atom.workspace.getFooterPanels === 'function') {
|
||||
const footerPanels = atom.workspace.getFooterPanels()
|
||||
const footerPanels = atom.workspace.getFooterPanels();
|
||||
if (footerPanels.length > 0) {
|
||||
return footerPanels[0].getItem()
|
||||
return footerPanels[0].getItem();
|
||||
}
|
||||
}
|
||||
|
||||
return atom.workspace.getBottomPanels()[0].getItem()
|
||||
return atom.workspace.getBottomPanels()[0].getItem();
|
||||
}
|
||||
|
||||
describe('Incompatible packages', () => {
|
||||
let statusBar
|
||||
let statusBar;
|
||||
|
||||
beforeEach(() => {
|
||||
atom.views.getView(atom.workspace)
|
||||
atom.views.getView(atom.workspace);
|
||||
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'))
|
||||
waitsForPromise(() => atom.packages.activatePackage('status-bar'));
|
||||
|
||||
runs(() => {
|
||||
statusBar = findStatusBar()
|
||||
})
|
||||
})
|
||||
statusBar = findStatusBar();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are packages with incompatible native modules', () => {
|
||||
beforeEach(() => {
|
||||
let incompatiblePackage = atom.packages.loadPackage(
|
||||
path.join(__dirname, 'fixtures', 'incompatible-package')
|
||||
)
|
||||
spyOn(incompatiblePackage, 'isCompatible').andReturn(false)
|
||||
incompatiblePackage.incompatibleModules = []
|
||||
);
|
||||
spyOn(incompatiblePackage, 'isCompatible').andReturn(false);
|
||||
incompatiblePackage.incompatibleModules = [];
|
||||
waitsForPromise(() =>
|
||||
atom.packages.activatePackage('incompatible-packages')
|
||||
)
|
||||
);
|
||||
|
||||
waits(1)
|
||||
})
|
||||
waits(1);
|
||||
});
|
||||
|
||||
it('adds an icon to the status bar', () => {
|
||||
let statusBarIcon = statusBar.getRightTiles()[0].getItem()
|
||||
expect(statusBarIcon.constructor).toBe(StatusIconComponent)
|
||||
})
|
||||
let statusBarIcon = statusBar.getRightTiles()[0].getItem();
|
||||
expect(statusBarIcon.constructor).toBe(StatusIconComponent);
|
||||
});
|
||||
|
||||
describe('clicking the icon', () => {
|
||||
it('displays the incompatible packages view in a pane', () => {
|
||||
let statusBarIcon = statusBar.getRightTiles()[0].getItem()
|
||||
statusBarIcon.element.dispatchEvent(new MouseEvent('click'))
|
||||
let statusBarIcon = statusBar.getRightTiles()[0].getItem();
|
||||
statusBarIcon.element.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
let activePaneItem
|
||||
waitsFor(() => (activePaneItem = atom.workspace.getActivePaneItem()))
|
||||
let activePaneItem;
|
||||
waitsFor(() => (activePaneItem = atom.workspace.getActivePaneItem()));
|
||||
|
||||
runs(() => {
|
||||
expect(activePaneItem.constructor).toBe(IncompatiblePackagesComponent)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(activePaneItem.constructor).toBe(
|
||||
IncompatiblePackagesComponent
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there are no packages with incompatible native modules', () => {
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() =>
|
||||
atom.packages.activatePackage('incompatible-packages')
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('does not add an icon to the status bar', () => {
|
||||
let statusBarItemClasses = statusBar
|
||||
.getRightTiles()
|
||||
.map(tile => tile.getItem().className)
|
||||
.map(tile => tile.getItem().className);
|
||||
|
||||
expect(statusBarItemClasses).not.toContain('incompatible-packages')
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(statusBarItemClasses).not.toContain('incompatible-packages');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use babel'
|
||||
'use babel';
|
||||
|
||||
export default {
|
||||
getProcessPlatform () {
|
||||
return process.platform
|
||||
getProcessPlatform() {
|
||||
return process.platform;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,26 +1,26 @@
|
||||
'use babel'
|
||||
'use babel';
|
||||
|
||||
import _ from 'underscore-plus'
|
||||
import { CompositeDisposable, Disposable } from 'atom'
|
||||
import SelectListView from 'atom-select-list'
|
||||
import StatusBarItem from './status-bar-item'
|
||||
import helpers from './helpers'
|
||||
import _ from 'underscore-plus';
|
||||
import { CompositeDisposable, Disposable } from 'atom';
|
||||
import SelectListView from 'atom-select-list';
|
||||
import StatusBarItem from './status-bar-item';
|
||||
import helpers from './helpers';
|
||||
|
||||
const LineEndingRegExp = /\r\n|\n/g
|
||||
const LineEndingRegExp = /\r\n|\n/g;
|
||||
|
||||
// the following regular expression is executed natively via the `substring` package,
|
||||
// where `\A` corresponds to the beginning of the string.
|
||||
// More info: https://github.com/atom/line-ending-selector/pull/56
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const LFRegExp = /(\A|[^\r])\n/g
|
||||
const CRLFRegExp = /\r\n/g
|
||||
const LFRegExp = /(\A|[^\r])\n/g;
|
||||
const CRLFRegExp = /\r\n/g;
|
||||
|
||||
let disposables = null
|
||||
let modalPanel = null
|
||||
let lineEndingListView = null
|
||||
let disposables = null;
|
||||
let modalPanel = null;
|
||||
let lineEndingListView = null;
|
||||
|
||||
export function activate () {
|
||||
disposables = new CompositeDisposable()
|
||||
export function activate() {
|
||||
disposables = new CompositeDisposable();
|
||||
|
||||
disposables.add(
|
||||
atom.commands.add('atom-text-editor', {
|
||||
@ -36,161 +36,161 @@ export function activate () {
|
||||
setLineEnding(
|
||||
atom.workspace.getActiveTextEditor(),
|
||||
lineEnding.value
|
||||
)
|
||||
modalPanel.hide()
|
||||
);
|
||||
modalPanel.hide();
|
||||
},
|
||||
didCancelSelection: () => {
|
||||
modalPanel.hide()
|
||||
modalPanel.hide();
|
||||
},
|
||||
elementForItem: lineEnding => {
|
||||
const element = document.createElement('li')
|
||||
element.textContent = lineEnding.name
|
||||
return element
|
||||
const element = document.createElement('li');
|
||||
element.textContent = lineEnding.name;
|
||||
return element;
|
||||
}
|
||||
})
|
||||
});
|
||||
modalPanel = atom.workspace.addModalPanel({
|
||||
item: lineEndingListView
|
||||
})
|
||||
});
|
||||
disposables.add(
|
||||
new Disposable(() => {
|
||||
lineEndingListView.destroy()
|
||||
modalPanel.destroy()
|
||||
modalPanel = null
|
||||
lineEndingListView.destroy();
|
||||
modalPanel.destroy();
|
||||
modalPanel = null;
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
lineEndingListView.reset()
|
||||
modalPanel.show()
|
||||
lineEndingListView.focus()
|
||||
lineEndingListView.reset();
|
||||
modalPanel.show();
|
||||
lineEndingListView.focus();
|
||||
},
|
||||
|
||||
'line-ending-selector:convert-to-LF': event => {
|
||||
const editorElement = event.target.closest('atom-text-editor')
|
||||
setLineEnding(editorElement.getModel(), '\n')
|
||||
const editorElement = event.target.closest('atom-text-editor');
|
||||
setLineEnding(editorElement.getModel(), '\n');
|
||||
},
|
||||
|
||||
'line-ending-selector:convert-to-CRLF': event => {
|
||||
const editorElement = event.target.closest('atom-text-editor')
|
||||
setLineEnding(editorElement.getModel(), '\r\n')
|
||||
const editorElement = event.target.closest('atom-text-editor');
|
||||
setLineEnding(editorElement.getModel(), '\r\n');
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate () {
|
||||
disposables.dispose()
|
||||
export function deactivate() {
|
||||
disposables.dispose();
|
||||
}
|
||||
|
||||
export function consumeStatusBar (statusBar) {
|
||||
let statusBarItem = new StatusBarItem()
|
||||
let currentBufferDisposable = null
|
||||
let tooltipDisposable = null
|
||||
export function consumeStatusBar(statusBar) {
|
||||
let statusBarItem = new StatusBarItem();
|
||||
let currentBufferDisposable = null;
|
||||
let tooltipDisposable = null;
|
||||
|
||||
const updateTile = _.debounce(buffer => {
|
||||
getLineEndings(buffer).then(lineEndings => {
|
||||
if (lineEndings.size === 0) {
|
||||
let defaultLineEnding = getDefaultLineEnding()
|
||||
buffer.setPreferredLineEnding(defaultLineEnding)
|
||||
lineEndings = new Set().add(defaultLineEnding)
|
||||
let defaultLineEnding = getDefaultLineEnding();
|
||||
buffer.setPreferredLineEnding(defaultLineEnding);
|
||||
lineEndings = new Set().add(defaultLineEnding);
|
||||
}
|
||||
statusBarItem.setLineEndings(lineEndings)
|
||||
})
|
||||
}, 0)
|
||||
statusBarItem.setLineEndings(lineEndings);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
disposables.add(
|
||||
atom.workspace.observeActiveTextEditor(editor => {
|
||||
if (currentBufferDisposable) currentBufferDisposable.dispose()
|
||||
if (currentBufferDisposable) currentBufferDisposable.dispose();
|
||||
|
||||
if (editor && editor.getBuffer) {
|
||||
let buffer = editor.getBuffer()
|
||||
updateTile(buffer)
|
||||
let buffer = editor.getBuffer();
|
||||
updateTile(buffer);
|
||||
currentBufferDisposable = buffer.onDidChange(({ oldText, newText }) => {
|
||||
if (!statusBarItem.hasLineEnding('\n')) {
|
||||
if (newText.indexOf('\n') >= 0) {
|
||||
updateTile(buffer)
|
||||
updateTile(buffer);
|
||||
}
|
||||
} else if (!statusBarItem.hasLineEnding('\r\n')) {
|
||||
if (newText.indexOf('\r\n') >= 0) {
|
||||
updateTile(buffer)
|
||||
updateTile(buffer);
|
||||
}
|
||||
} else if (oldText.indexOf('\n')) {
|
||||
updateTile(buffer)
|
||||
updateTile(buffer);
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
statusBarItem.setLineEndings(new Set())
|
||||
currentBufferDisposable = null
|
||||
statusBarItem.setLineEndings(new Set());
|
||||
currentBufferDisposable = null;
|
||||
}
|
||||
|
||||
if (tooltipDisposable) {
|
||||
disposables.remove(tooltipDisposable)
|
||||
tooltipDisposable.dispose()
|
||||
disposables.remove(tooltipDisposable);
|
||||
tooltipDisposable.dispose();
|
||||
}
|
||||
tooltipDisposable = atom.tooltips.add(statusBarItem.element, {
|
||||
title () {
|
||||
return `File uses ${statusBarItem.description()} line endings`
|
||||
title() {
|
||||
return `File uses ${statusBarItem.description()} line endings`;
|
||||
}
|
||||
})
|
||||
disposables.add(tooltipDisposable)
|
||||
});
|
||||
disposables.add(tooltipDisposable);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
disposables.add(
|
||||
new Disposable(() => {
|
||||
if (currentBufferDisposable) currentBufferDisposable.dispose()
|
||||
if (currentBufferDisposable) currentBufferDisposable.dispose();
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
statusBarItem.onClick(() => {
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
atom.commands.dispatch(
|
||||
atom.views.getView(editor),
|
||||
'line-ending-selector:show'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
let tile = statusBar.addRightTile({ item: statusBarItem, priority: 200 })
|
||||
disposables.add(new Disposable(() => tile.destroy()))
|
||||
let tile = statusBar.addRightTile({ item: statusBarItem, priority: 200 });
|
||||
disposables.add(new Disposable(() => tile.destroy()));
|
||||
}
|
||||
|
||||
function getDefaultLineEnding () {
|
||||
function getDefaultLineEnding() {
|
||||
switch (atom.config.get('line-ending-selector.defaultLineEnding')) {
|
||||
case 'LF':
|
||||
return '\n'
|
||||
return '\n';
|
||||
case 'CRLF':
|
||||
return '\r\n'
|
||||
return '\r\n';
|
||||
case 'OS Default':
|
||||
default:
|
||||
return helpers.getProcessPlatform() === 'win32' ? '\r\n' : '\n'
|
||||
return helpers.getProcessPlatform() === 'win32' ? '\r\n' : '\n';
|
||||
}
|
||||
}
|
||||
|
||||
function getLineEndings (buffer) {
|
||||
function getLineEndings(buffer) {
|
||||
if (typeof buffer.find === 'function') {
|
||||
return Promise.all([buffer.find(LFRegExp), buffer.find(CRLFRegExp)]).then(
|
||||
([hasLF, hasCRLF]) => {
|
||||
const result = new Set()
|
||||
if (hasLF) result.add('\n')
|
||||
if (hasCRLF) result.add('\r\n')
|
||||
return result
|
||||
const result = new Set();
|
||||
if (hasLF) result.add('\n');
|
||||
if (hasCRLF) result.add('\r\n');
|
||||
return result;
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
const result = new Set()
|
||||
const result = new Set();
|
||||
for (let i = 0; i < buffer.getLineCount() - 1; i++) {
|
||||
result.add(buffer.lineEndingForRow(i))
|
||||
result.add(buffer.lineEndingForRow(i));
|
||||
}
|
||||
resolve(result)
|
||||
})
|
||||
resolve(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setLineEnding (item, lineEnding) {
|
||||
function setLineEnding(item, lineEnding) {
|
||||
if (item && item.getBuffer) {
|
||||
let buffer = item.getBuffer()
|
||||
buffer.setPreferredLineEnding(lineEnding)
|
||||
buffer.setText(buffer.getText().replace(LineEndingRegExp, lineEnding))
|
||||
let buffer = item.getBuffer();
|
||||
buffer.setPreferredLineEnding(lineEnding);
|
||||
buffer.setText(buffer.getText().replace(LineEndingRegExp, lineEnding));
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,57 @@
|
||||
const { Emitter } = require('atom')
|
||||
const { Emitter } = require('atom');
|
||||
|
||||
module.exports = class StatusBarItem {
|
||||
constructor () {
|
||||
this.element = document.createElement('a')
|
||||
this.element.className = 'line-ending-tile inline-block'
|
||||
this.emitter = new Emitter()
|
||||
this.setLineEndings(new Set())
|
||||
constructor() {
|
||||
this.element = document.createElement('a');
|
||||
this.element.className = 'line-ending-tile inline-block';
|
||||
this.emitter = new Emitter();
|
||||
this.setLineEndings(new Set());
|
||||
}
|
||||
|
||||
setLineEndings (lineEndings) {
|
||||
this.lineEndings = lineEndings
|
||||
this.element.textContent = lineEndingName(lineEndings)
|
||||
this.emitter.emit('did-change')
|
||||
setLineEndings(lineEndings) {
|
||||
this.lineEndings = lineEndings;
|
||||
this.element.textContent = lineEndingName(lineEndings);
|
||||
this.emitter.emit('did-change');
|
||||
}
|
||||
|
||||
onDidChange (callback) {
|
||||
return this.emitter.on('did-change', callback)
|
||||
onDidChange(callback) {
|
||||
return this.emitter.on('did-change', callback);
|
||||
}
|
||||
|
||||
hasLineEnding (lineEnding) {
|
||||
return this.lineEndings.has(lineEnding)
|
||||
hasLineEnding(lineEnding) {
|
||||
return this.lineEndings.has(lineEnding);
|
||||
}
|
||||
|
||||
description () {
|
||||
return lineEndingDescription(this.lineEndings)
|
||||
description() {
|
||||
return lineEndingDescription(this.lineEndings);
|
||||
}
|
||||
|
||||
onClick (callback) {
|
||||
this.element.addEventListener('click', callback)
|
||||
onClick(callback) {
|
||||
this.element.addEventListener('click', callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function lineEndingName (lineEndings) {
|
||||
function lineEndingName(lineEndings) {
|
||||
if (lineEndings.size > 1) {
|
||||
return 'Mixed'
|
||||
return 'Mixed';
|
||||
} else if (lineEndings.has('\n')) {
|
||||
return 'LF'
|
||||
return 'LF';
|
||||
} else if (lineEndings.has('\r\n')) {
|
||||
return 'CRLF'
|
||||
return 'CRLF';
|
||||
} else {
|
||||
return ''
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function lineEndingDescription (lineEndings) {
|
||||
function lineEndingDescription(lineEndings) {
|
||||
switch (lineEndingName(lineEndings)) {
|
||||
case 'Mixed':
|
||||
return 'mixed'
|
||||
return 'mixed';
|
||||
case 'LF':
|
||||
return 'LF (Unix)'
|
||||
return 'LF (Unix)';
|
||||
case 'CRLF':
|
||||
return 'CRLF (Windows)'
|
||||
return 'CRLF (Windows)';
|
||||
default:
|
||||
return 'unknown'
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
@ -1,374 +1,378 @@
|
||||
const helpers = require('../lib/helpers')
|
||||
const { TextEditor } = require('atom')
|
||||
const helpers = require('../lib/helpers');
|
||||
const { TextEditor } = require('atom');
|
||||
|
||||
describe('line ending selector', () => {
|
||||
let lineEndingTile
|
||||
let lineEndingTile;
|
||||
|
||||
beforeEach(() => {
|
||||
jasmine.useRealClock()
|
||||
jasmine.useRealClock();
|
||||
|
||||
waitsForPromise(() => {
|
||||
return atom.packages.activatePackage('status-bar')
|
||||
})
|
||||
return atom.packages.activatePackage('status-bar');
|
||||
});
|
||||
|
||||
waitsForPromise(() => {
|
||||
return atom.packages.activatePackage('line-ending-selector')
|
||||
})
|
||||
return atom.packages.activatePackage('line-ending-selector');
|
||||
});
|
||||
|
||||
waits(1)
|
||||
waits(1);
|
||||
|
||||
runs(() => {
|
||||
const statusBar = atom.workspace.getFooterPanels()[0].getItem()
|
||||
lineEndingTile = statusBar.getRightTiles()[0].getItem()
|
||||
expect(lineEndingTile.element.className).toMatch(/line-ending-tile/)
|
||||
expect(lineEndingTile.element.textContent).toBe('')
|
||||
})
|
||||
})
|
||||
const statusBar = atom.workspace.getFooterPanels()[0].getItem();
|
||||
lineEndingTile = statusBar.getRightTiles()[0].getItem();
|
||||
expect(lineEndingTile.element.className).toMatch(/line-ending-tile/);
|
||||
expect(lineEndingTile.element.textContent).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Commands', () => {
|
||||
let editor, editorElement
|
||||
let editor, editorElement;
|
||||
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() => {
|
||||
return atom.workspace.open('mixed-endings.md').then(e => {
|
||||
editor = e
|
||||
editorElement = atom.views.getView(editor)
|
||||
jasmine.attachToDOM(editorElement)
|
||||
})
|
||||
})
|
||||
})
|
||||
editor = e;
|
||||
editorElement = atom.views.getView(editor);
|
||||
jasmine.attachToDOM(editorElement);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When "line-ending-selector:convert-to-LF" is run', () => {
|
||||
it('converts the file to LF line endings', () => {
|
||||
editorElement.focus()
|
||||
editorElement.focus();
|
||||
atom.commands.dispatch(
|
||||
document.activeElement,
|
||||
'line-ending-selector:convert-to-LF'
|
||||
)
|
||||
expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n')
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When "line-ending-selector:convert-to-LF" is run', () => {
|
||||
it('converts the file to CRLF line endings', () => {
|
||||
editorElement.focus()
|
||||
editorElement.focus();
|
||||
atom.commands.dispatch(
|
||||
document.activeElement,
|
||||
'line-ending-selector:convert-to-CRLF'
|
||||
)
|
||||
expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n')
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Status bar tile', () => {
|
||||
describe('when an empty file is opened', () => {
|
||||
it('uses the default line endings for the platform', () => {
|
||||
waitsFor(done => {
|
||||
spyOn(helpers, 'getProcessPlatform').andReturn('win32')
|
||||
spyOn(helpers, 'getProcessPlatform').andReturn('win32');
|
||||
|
||||
atom.workspace.open('').then(editor => {
|
||||
const subscription = lineEndingTile.onDidChange(() => {
|
||||
subscription.dispose()
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
|
||||
subscription.dispose();
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n');
|
||||
expect(getTooltipText(lineEndingTile.element)).toBe(
|
||||
'File uses CRLF (Windows) line endings'
|
||||
)
|
||||
);
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
waitsFor(done => {
|
||||
helpers.getProcessPlatform.andReturn('darwin')
|
||||
helpers.getProcessPlatform.andReturn('darwin');
|
||||
|
||||
atom.workspace.open('').then(editor => {
|
||||
const subscription = lineEndingTile.onDidChange(() => {
|
||||
subscription.dispose()
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
|
||||
subscription.dispose();
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n');
|
||||
expect(getTooltipText(lineEndingTile.element)).toBe(
|
||||
'File uses LF (Unix) line endings'
|
||||
)
|
||||
);
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the "defaultLineEnding" setting is set to "LF"', () => {
|
||||
beforeEach(() => {
|
||||
atom.config.set('line-ending-selector.defaultLineEnding', 'LF')
|
||||
})
|
||||
atom.config.set('line-ending-selector.defaultLineEnding', 'LF');
|
||||
});
|
||||
|
||||
it('uses LF line endings, regardless of the platform', () => {
|
||||
waitsFor(done => {
|
||||
spyOn(helpers, 'getProcessPlatform').andReturn('win32')
|
||||
spyOn(helpers, 'getProcessPlatform').andReturn('win32');
|
||||
|
||||
atom.workspace.open('').then(editor => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the "defaultLineEnding" setting is set to "CRLF"', () => {
|
||||
beforeEach(() => {
|
||||
atom.config.set('line-ending-selector.defaultLineEnding', 'CRLF')
|
||||
})
|
||||
atom.config.set('line-ending-selector.defaultLineEnding', 'CRLF');
|
||||
});
|
||||
|
||||
it('uses CRLF line endings, regardless of the platform', () => {
|
||||
waitsFor(done => {
|
||||
atom.workspace.open('').then(editor => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe(
|
||||
'\r\n'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a file is opened that contains only CRLF line endings', () => {
|
||||
it('displays "CRLF" as the line ending', () => {
|
||||
waitsFor(done => {
|
||||
atom.workspace.open('windows-endings.md').then(() => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a file is opened that contains only LF line endings', () => {
|
||||
it('displays "LF" as the line ending', () => {
|
||||
waitsFor(done => {
|
||||
atom.workspace.open('unix-endings.md').then(editor => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe(null)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a file is opened that contains mixed line endings', () => {
|
||||
it('displays "Mixed" as the line ending', () => {
|
||||
waitsFor(done => {
|
||||
atom.workspace.open('mixed-endings.md').then(() => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('Mixed')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('Mixed');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clicking the tile', () => {
|
||||
let lineEndingModal, lineEndingSelector
|
||||
let lineEndingModal, lineEndingSelector;
|
||||
|
||||
beforeEach(() => {
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace))
|
||||
jasmine.attachToDOM(atom.views.getView(atom.workspace));
|
||||
|
||||
waitsFor(done =>
|
||||
atom.workspace
|
||||
.open('unix-endings.md')
|
||||
.then(() => lineEndingTile.onDidChange(done))
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('when the text editor has focus', () => {
|
||||
it('opens the line ending selector modal for the text editor', () => {
|
||||
atom.workspace.getCenter().activate()
|
||||
const item = atom.workspace.getActivePaneItem()
|
||||
expect(item.getFileName && item.getFileName()).toBe('unix-endings.md')
|
||||
atom.workspace.getCenter().activate();
|
||||
const item = atom.workspace.getActivePaneItem();
|
||||
expect(item.getFileName && item.getFileName()).toBe(
|
||||
'unix-endings.md'
|
||||
);
|
||||
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}))
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0]
|
||||
lineEndingSelector = lineEndingModal.getItem()
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0];
|
||||
lineEndingSelector = lineEndingModal.getItem();
|
||||
|
||||
expect(lineEndingModal.isVisible()).toBe(true)
|
||||
expect(lineEndingModal.isVisible()).toBe(true);
|
||||
expect(
|
||||
lineEndingSelector.element.contains(document.activeElement)
|
||||
).toBe(true)
|
||||
let listItems = lineEndingSelector.element.querySelectorAll('li')
|
||||
expect(listItems[0].textContent).toBe('LF')
|
||||
expect(listItems[1].textContent).toBe('CRLF')
|
||||
})
|
||||
})
|
||||
).toBe(true);
|
||||
let listItems = lineEndingSelector.element.querySelectorAll('li');
|
||||
expect(listItems[0].textContent).toBe('LF');
|
||||
expect(listItems[1].textContent).toBe('CRLF');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the text editor does not have focus', () => {
|
||||
it('opens the line ending selector modal for the active text editor', () => {
|
||||
atom.workspace.getLeftDock().activate()
|
||||
const item = atom.workspace.getActivePaneItem()
|
||||
expect(item instanceof TextEditor).toBe(false)
|
||||
atom.workspace.getLeftDock().activate();
|
||||
const item = atom.workspace.getActivePaneItem();
|
||||
expect(item instanceof TextEditor).toBe(false);
|
||||
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}))
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0]
|
||||
lineEndingSelector = lineEndingModal.getItem()
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0];
|
||||
lineEndingSelector = lineEndingModal.getItem();
|
||||
|
||||
expect(lineEndingModal.isVisible()).toBe(true)
|
||||
expect(lineEndingModal.isVisible()).toBe(true);
|
||||
expect(
|
||||
lineEndingSelector.element.contains(document.activeElement)
|
||||
).toBe(true)
|
||||
let listItems = lineEndingSelector.element.querySelectorAll('li')
|
||||
expect(listItems[0].textContent).toBe('LF')
|
||||
expect(listItems[1].textContent).toBe('CRLF')
|
||||
})
|
||||
})
|
||||
).toBe(true);
|
||||
let listItems = lineEndingSelector.element.querySelectorAll('li');
|
||||
expect(listItems[0].textContent).toBe('LF');
|
||||
expect(listItems[1].textContent).toBe('CRLF');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selecting a different line ending for the file', () => {
|
||||
it('changes the line endings in the buffer', () => {
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}))
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0]
|
||||
lineEndingSelector = lineEndingModal.getItem()
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0];
|
||||
lineEndingSelector = lineEndingModal.getItem();
|
||||
|
||||
const lineEndingChangedPromise = new Promise(resolve => {
|
||||
lineEndingTile.onDidChange(() => {
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF')
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nUnix\r\n')
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF');
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nUnix\r\n');
|
||||
expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
lineEndingSelector.refs.queryEditor.setText('CR')
|
||||
lineEndingSelector.confirmSelection()
|
||||
expect(lineEndingModal.isVisible()).toBe(false)
|
||||
lineEndingSelector.refs.queryEditor.setText('CR');
|
||||
lineEndingSelector.confirmSelection();
|
||||
expect(lineEndingModal.isVisible()).toBe(false);
|
||||
|
||||
waitsForPromise(() => lineEndingChangedPromise)
|
||||
})
|
||||
})
|
||||
waitsForPromise(() => lineEndingChangedPromise);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when modal is exited', () => {
|
||||
it('leaves the tile selection as-is', () => {
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}))
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0]
|
||||
lineEndingSelector = lineEndingModal.getItem()
|
||||
lineEndingTile.element.dispatchEvent(new MouseEvent('click', {}));
|
||||
lineEndingModal = atom.workspace.getModalPanels()[0];
|
||||
lineEndingSelector = lineEndingModal.getItem();
|
||||
|
||||
lineEndingSelector.cancelSelection()
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
})
|
||||
})
|
||||
})
|
||||
lineEndingSelector.cancelSelection();
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('closing the last text editor', () => {
|
||||
it('displays no line ending in the status bar', () => {
|
||||
waitsForPromise(() => {
|
||||
return atom.workspace.open('unix-endings.md').then(() => {
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(lineEndingTile.element.textContent).toBe('')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
atom.workspace.getActivePane().destroy();
|
||||
expect(lineEndingTile.element.textContent).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the buffer's line endings change", () => {
|
||||
let editor
|
||||
let editor;
|
||||
|
||||
beforeEach(() => {
|
||||
waitsFor(done => {
|
||||
atom.workspace.open('unix-endings.md').then(e => {
|
||||
editor = e
|
||||
lineEndingTile.onDidChange(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
editor = e;
|
||||
lineEndingTile.onDidChange(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the line ending text in the tile', () => {
|
||||
let tileText = lineEndingTile.element.textContent
|
||||
let tileUpdateCount = 0
|
||||
let tileText = lineEndingTile.element.textContent;
|
||||
let tileUpdateCount = 0;
|
||||
Object.defineProperty(lineEndingTile.element, 'textContent', {
|
||||
get () {
|
||||
return tileText
|
||||
get() {
|
||||
return tileText;
|
||||
},
|
||||
|
||||
set (text) {
|
||||
tileUpdateCount++
|
||||
tileText = text
|
||||
set(text) {
|
||||
tileUpdateCount++;
|
||||
tileText = text;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
expect(getTooltipText(lineEndingTile.element)).toBe(
|
||||
'File uses LF (Unix) line endings'
|
||||
)
|
||||
);
|
||||
|
||||
waitsFor(done => {
|
||||
editor.setTextInBufferRange([[0, 0], [0, 0]], '... ')
|
||||
editor.setTextInBufferRange([[0, 0], [0, 0]], '... ');
|
||||
editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', {
|
||||
normalizeLineEndings: false
|
||||
})
|
||||
lineEndingTile.onDidChange(done)
|
||||
})
|
||||
});
|
||||
lineEndingTile.onDidChange(done);
|
||||
});
|
||||
|
||||
runs(() => {
|
||||
expect(tileUpdateCount).toBe(1)
|
||||
expect(lineEndingTile.element.textContent).toBe('Mixed')
|
||||
expect(tileUpdateCount).toBe(1);
|
||||
expect(lineEndingTile.element.textContent).toBe('Mixed');
|
||||
expect(getTooltipText(lineEndingTile.element)).toBe(
|
||||
'File uses mixed line endings'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
waitsFor(done => {
|
||||
atom.commands.dispatch(
|
||||
editor.getElement(),
|
||||
'line-ending-selector:convert-to-CRLF'
|
||||
)
|
||||
lineEndingTile.onDidChange(done)
|
||||
})
|
||||
);
|
||||
lineEndingTile.onDidChange(done);
|
||||
});
|
||||
|
||||
runs(() => {
|
||||
expect(tileUpdateCount).toBe(2)
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF')
|
||||
expect(tileUpdateCount).toBe(2);
|
||||
expect(lineEndingTile.element.textContent).toBe('CRLF');
|
||||
expect(getTooltipText(lineEndingTile.element)).toBe(
|
||||
'File uses CRLF (Windows) line endings'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
waitsFor(done => {
|
||||
atom.commands.dispatch(
|
||||
editor.getElement(),
|
||||
'line-ending-selector:convert-to-LF'
|
||||
)
|
||||
lineEndingTile.onDidChange(done)
|
||||
})
|
||||
);
|
||||
lineEndingTile.onDidChange(done);
|
||||
});
|
||||
|
||||
runs(() => {
|
||||
expect(tileUpdateCount).toBe(3)
|
||||
expect(lineEndingTile.element.textContent).toBe('LF')
|
||||
})
|
||||
expect(tileUpdateCount).toBe(3);
|
||||
expect(lineEndingTile.element.textContent).toBe('LF');
|
||||
});
|
||||
|
||||
runs(() => {
|
||||
editor.setTextInBufferRange([[0, 0], [0, 0]], '\n')
|
||||
})
|
||||
editor.setTextInBufferRange([[0, 0], [0, 0]], '\n');
|
||||
});
|
||||
|
||||
waits(100)
|
||||
waits(100);
|
||||
|
||||
runs(() => {
|
||||
expect(tileUpdateCount).toBe(3)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(tileUpdateCount).toBe(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getTooltipText (element) {
|
||||
const [tooltip] = atom.tooltips.findTooltips(element)
|
||||
return tooltip.getTitle()
|
||||
function getTooltipText(element) {
|
||||
const [tooltip] = atom.tooltips.findTooltips(element);
|
||||
return tooltip.getTitle();
|
||||
}
|
||||
|
@ -1,64 +1,64 @@
|
||||
const url = require('url')
|
||||
const { shell } = require('electron')
|
||||
const _ = require('underscore-plus')
|
||||
const url = require('url');
|
||||
const { shell } = require('electron');
|
||||
const _ = require('underscore-plus');
|
||||
|
||||
const LINK_SCOPE_REGEX = /markup\.underline\.link/
|
||||
const LINK_SCOPE_REGEX = /markup\.underline\.link/;
|
||||
|
||||
module.exports = {
|
||||
activate () {
|
||||
activate() {
|
||||
this.commandDisposable = atom.commands.add(
|
||||
'atom-text-editor',
|
||||
'link:open',
|
||||
() => this.openLink()
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
this.commandDisposable.dispose()
|
||||
deactivate() {
|
||||
this.commandDisposable.dispose();
|
||||
},
|
||||
|
||||
openLink () {
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
if (editor == null) return
|
||||
openLink() {
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
if (editor == null) return;
|
||||
|
||||
let link = this.linkUnderCursor(editor)
|
||||
if (link == null) return
|
||||
let link = this.linkUnderCursor(editor);
|
||||
if (link == null) return;
|
||||
|
||||
if (editor.getGrammar().scopeName === 'source.gfm') {
|
||||
link = this.linkForName(editor, link)
|
||||
link = this.linkForName(editor, link);
|
||||
}
|
||||
|
||||
const { protocol } = url.parse(link)
|
||||
const { protocol } = url.parse(link);
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'atom:') {
|
||||
shell.openExternal(link)
|
||||
shell.openExternal(link);
|
||||
}
|
||||
},
|
||||
|
||||
// Get the link under the cursor in the editor
|
||||
//
|
||||
// Returns a {String} link or undefined if no link found.
|
||||
linkUnderCursor (editor) {
|
||||
const cursorPosition = editor.getCursorBufferPosition()
|
||||
const link = this.linkAtPosition(editor, cursorPosition)
|
||||
if (link != null) return link
|
||||
linkUnderCursor(editor) {
|
||||
const cursorPosition = editor.getCursorBufferPosition();
|
||||
const link = this.linkAtPosition(editor, cursorPosition);
|
||||
if (link != null) return link;
|
||||
|
||||
// Look for a link to the left of the cursor
|
||||
if (cursorPosition.column > 0) {
|
||||
return this.linkAtPosition(editor, cursorPosition.translate([0, -1]))
|
||||
return this.linkAtPosition(editor, cursorPosition.translate([0, -1]));
|
||||
}
|
||||
},
|
||||
|
||||
// Get the link at the buffer position in the editor.
|
||||
//
|
||||
// Returns a {String} link or undefined if no link found.
|
||||
linkAtPosition (editor, bufferPosition) {
|
||||
const token = editor.tokenForBufferPosition(bufferPosition)
|
||||
linkAtPosition(editor, bufferPosition) {
|
||||
const token = editor.tokenForBufferPosition(bufferPosition);
|
||||
if (
|
||||
token &&
|
||||
token.value &&
|
||||
token.scopes.some(scope => LINK_SCOPE_REGEX.test(scope))
|
||||
) {
|
||||
return token.value
|
||||
return token.value;
|
||||
}
|
||||
},
|
||||
|
||||
@ -73,20 +73,20 @@ module.exports = {
|
||||
// ```
|
||||
//
|
||||
// Returns a {String} link
|
||||
linkForName (editor, linkName) {
|
||||
let link = linkName
|
||||
linkForName(editor, linkName) {
|
||||
let link = linkName;
|
||||
const regex = new RegExp(
|
||||
`^\\s*\\[${_.escapeRegExp(linkName)}\\]\\s*:\\s*(.+)$`,
|
||||
'g'
|
||||
)
|
||||
);
|
||||
editor.backwardsScanInBufferRange(
|
||||
regex,
|
||||
[[0, 0], [Infinity, Infinity]],
|
||||
({ match, stop }) => {
|
||||
link = match[1]
|
||||
stop()
|
||||
link = match[1];
|
||||
stop();
|
||||
}
|
||||
)
|
||||
return link
|
||||
);
|
||||
return link;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,136 +1,136 @@
|
||||
const { shell } = require('electron')
|
||||
const { shell } = require('electron');
|
||||
|
||||
describe('link package', () => {
|
||||
beforeEach(async () => {
|
||||
await atom.packages.activatePackage('language-gfm')
|
||||
await atom.packages.activatePackage('language-hyperlink')
|
||||
await atom.packages.activatePackage('language-gfm');
|
||||
await atom.packages.activatePackage('language-hyperlink');
|
||||
|
||||
const activationPromise = atom.packages.activatePackage('link')
|
||||
atom.commands.dispatch(atom.views.getView(atom.workspace), 'link:open')
|
||||
await activationPromise
|
||||
})
|
||||
const activationPromise = atom.packages.activatePackage('link');
|
||||
atom.commands.dispatch(atom.views.getView(atom.workspace), 'link:open');
|
||||
await activationPromise;
|
||||
});
|
||||
|
||||
describe('when the cursor is on a link', () => {
|
||||
it("opens the link using the 'open' command", async () => {
|
||||
await atom.workspace.open('sample.md')
|
||||
await atom.workspace.open('sample.md');
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
editor.setText('// "http://github.com"')
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText('// "http://github.com"');
|
||||
|
||||
spyOn(shell, 'openExternal')
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
spyOn(shell, 'openExternal');
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
|
||||
editor.setCursorBufferPosition([0, 4])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
editor.setCursorBufferPosition([0, 4]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com')
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com');
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([0, 8])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
shell.openExternal.reset();
|
||||
editor.setCursorBufferPosition([0, 8]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com')
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com');
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([0, 21])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
shell.openExternal.reset();
|
||||
editor.setCursorBufferPosition([0, 21]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com')
|
||||
})
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com');
|
||||
});
|
||||
|
||||
// only works in Atom >= 1.33.0
|
||||
// https://github.com/atom/link/pull/33#issuecomment-419643655
|
||||
const atomVersion = atom.getVersion().split('.')
|
||||
console.error('atomVersion', atomVersion)
|
||||
const atomVersion = atom.getVersion().split('.');
|
||||
console.error('atomVersion', atomVersion);
|
||||
if (+atomVersion[0] > 1 || +atomVersion[1] >= 33) {
|
||||
it("opens an 'atom:' link", async () => {
|
||||
await atom.workspace.open('sample.md')
|
||||
await atom.workspace.open('sample.md');
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText(
|
||||
'// "atom://core/open/file?filename=sample.js&line=1&column=2"'
|
||||
)
|
||||
);
|
||||
|
||||
spyOn(shell, 'openExternal')
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
spyOn(shell, 'openExternal');
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
|
||||
editor.setCursorBufferPosition([0, 4])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
editor.setCursorBufferPosition([0, 4]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe(
|
||||
'atom://core/open/file?filename=sample.js&line=1&column=2'
|
||||
)
|
||||
);
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([0, 8])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
shell.openExternal.reset();
|
||||
editor.setCursorBufferPosition([0, 8]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe(
|
||||
'atom://core/open/file?filename=sample.js&line=1&column=2'
|
||||
)
|
||||
);
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([0, 60])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
shell.openExternal.reset();
|
||||
editor.setCursorBufferPosition([0, 60]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe(
|
||||
'atom://core/open/file?filename=sample.js&line=1&column=2'
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
describe('when the cursor is on a [name][url-name] style markdown link', () =>
|
||||
it('opens the named url', async () => {
|
||||
await atom.workspace.open('README.md')
|
||||
await atom.workspace.open('README.md');
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText(`\
|
||||
you should [click][here]
|
||||
you should not [click][her]
|
||||
|
||||
[here]: http://github.com\
|
||||
`)
|
||||
`);
|
||||
|
||||
spyOn(shell, 'openExternal')
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
spyOn(shell, 'openExternal');
|
||||
editor.setCursorBufferPosition([0, 0]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
|
||||
editor.setCursorBufferPosition([0, 20])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
editor.setCursorBufferPosition([0, 20]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com')
|
||||
expect(shell.openExternal).toHaveBeenCalled();
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe('http://github.com');
|
||||
|
||||
shell.openExternal.reset()
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
shell.openExternal.reset();
|
||||
editor.setCursorBufferPosition([1, 24]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
}))
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('does not open non http/https/atom links', async () => {
|
||||
await atom.workspace.open('sample.md')
|
||||
await atom.workspace.open('sample.md');
|
||||
|
||||
const editor = atom.workspace.getActiveTextEditor()
|
||||
editor.setText('// ftp://github.com\n')
|
||||
const editor = atom.workspace.getActiveTextEditor();
|
||||
editor.setText('// ftp://github.com\n');
|
||||
|
||||
spyOn(shell, 'openExternal')
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
spyOn(shell, 'openExternal');
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
|
||||
editor.setCursorBufferPosition([0, 5])
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open')
|
||||
editor.setCursorBufferPosition([0, 5]);
|
||||
atom.commands.dispatch(atom.views.getView(editor), 'link:open');
|
||||
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(shell.openExternal).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,88 +1,88 @@
|
||||
const root = document.documentElement
|
||||
const themeName = 'one-dark-ui'
|
||||
const root = document.documentElement;
|
||||
const themeName = 'one-dark-ui';
|
||||
|
||||
module.exports = {
|
||||
activate (state) {
|
||||
atom.config.observe(`${themeName}.fontSize`, setFontSize)
|
||||
atom.config.observe(`${themeName}.tabSizing`, setTabSizing)
|
||||
atom.config.observe(`${themeName}.tabCloseButton`, setTabCloseButton)
|
||||
atom.config.observe(`${themeName}.hideDockButtons`, setHideDockButtons)
|
||||
atom.config.observe(`${themeName}.stickyHeaders`, setStickyHeaders)
|
||||
activate(state) {
|
||||
atom.config.observe(`${themeName}.fontSize`, setFontSize);
|
||||
atom.config.observe(`${themeName}.tabSizing`, setTabSizing);
|
||||
atom.config.observe(`${themeName}.tabCloseButton`, setTabCloseButton);
|
||||
atom.config.observe(`${themeName}.hideDockButtons`, setHideDockButtons);
|
||||
atom.config.observe(`${themeName}.stickyHeaders`, setStickyHeaders);
|
||||
|
||||
// DEPRECATED: This can be removed at some point (added in Atom 1.17/1.18ish)
|
||||
// It removes `layoutMode`
|
||||
if (atom.config.get(`${themeName}.layoutMode`)) {
|
||||
atom.config.unset(`${themeName}.layoutMode`)
|
||||
atom.config.unset(`${themeName}.layoutMode`);
|
||||
}
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
unsetFontSize()
|
||||
unsetTabSizing()
|
||||
unsetTabCloseButton()
|
||||
unsetHideDockButtons()
|
||||
unsetStickyHeaders()
|
||||
deactivate() {
|
||||
unsetFontSize();
|
||||
unsetTabSizing();
|
||||
unsetTabCloseButton();
|
||||
unsetHideDockButtons();
|
||||
unsetStickyHeaders();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Font Size -----------------------
|
||||
|
||||
function setFontSize (currentFontSize) {
|
||||
root.style.fontSize = `${currentFontSize}px`
|
||||
function setFontSize(currentFontSize) {
|
||||
root.style.fontSize = `${currentFontSize}px`;
|
||||
}
|
||||
|
||||
function unsetFontSize () {
|
||||
root.style.fontSize = ''
|
||||
function unsetFontSize() {
|
||||
root.style.fontSize = '';
|
||||
}
|
||||
|
||||
// Tab Sizing -----------------------
|
||||
|
||||
function setTabSizing (tabSizing) {
|
||||
root.setAttribute(`theme-${themeName}-tabsizing`, tabSizing.toLowerCase())
|
||||
function setTabSizing(tabSizing) {
|
||||
root.setAttribute(`theme-${themeName}-tabsizing`, tabSizing.toLowerCase());
|
||||
}
|
||||
|
||||
function unsetTabSizing () {
|
||||
root.removeAttribute(`theme-${themeName}-tabsizing`)
|
||||
function unsetTabSizing() {
|
||||
root.removeAttribute(`theme-${themeName}-tabsizing`);
|
||||
}
|
||||
|
||||
// Tab Close Button -----------------------
|
||||
|
||||
function setTabCloseButton (tabCloseButton) {
|
||||
function setTabCloseButton(tabCloseButton) {
|
||||
if (tabCloseButton === 'Left') {
|
||||
root.setAttribute(`theme-${themeName}-tab-close-button`, 'left')
|
||||
root.setAttribute(`theme-${themeName}-tab-close-button`, 'left');
|
||||
} else {
|
||||
unsetTabCloseButton()
|
||||
unsetTabCloseButton();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetTabCloseButton () {
|
||||
root.removeAttribute(`theme-${themeName}-tab-close-button`)
|
||||
function unsetTabCloseButton() {
|
||||
root.removeAttribute(`theme-${themeName}-tab-close-button`);
|
||||
}
|
||||
|
||||
// Dock Buttons -----------------------
|
||||
|
||||
function setHideDockButtons (hideDockButtons) {
|
||||
function setHideDockButtons(hideDockButtons) {
|
||||
if (hideDockButtons) {
|
||||
root.setAttribute(`theme-${themeName}-dock-buttons`, 'hidden')
|
||||
root.setAttribute(`theme-${themeName}-dock-buttons`, 'hidden');
|
||||
} else {
|
||||
unsetHideDockButtons()
|
||||
unsetHideDockButtons();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetHideDockButtons () {
|
||||
root.removeAttribute(`theme-${themeName}-dock-buttons`)
|
||||
function unsetHideDockButtons() {
|
||||
root.removeAttribute(`theme-${themeName}-dock-buttons`);
|
||||
}
|
||||
|
||||
// Sticky Headers -----------------------
|
||||
|
||||
function setStickyHeaders (stickyHeaders) {
|
||||
function setStickyHeaders(stickyHeaders) {
|
||||
if (stickyHeaders) {
|
||||
root.setAttribute(`theme-${themeName}-sticky-headers`, 'sticky')
|
||||
root.setAttribute(`theme-${themeName}-sticky-headers`, 'sticky');
|
||||
} else {
|
||||
unsetStickyHeaders()
|
||||
unsetStickyHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetStickyHeaders () {
|
||||
root.removeAttribute(`theme-${themeName}-sticky-headers`)
|
||||
function unsetStickyHeaders() {
|
||||
root.removeAttribute(`theme-${themeName}-sticky-headers`);
|
||||
}
|
||||
|
@ -1,58 +1,58 @@
|
||||
const themeName = 'one-dark-ui'
|
||||
const themeName = 'one-dark-ui';
|
||||
|
||||
describe(`${themeName} theme`, () => {
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() => atom.packages.activatePackage(themeName))
|
||||
})
|
||||
waitsForPromise(() => atom.packages.activatePackage(themeName));
|
||||
});
|
||||
|
||||
it('allows the font size to be set via config', () => {
|
||||
expect(document.documentElement.style.fontSize).toBe('12px')
|
||||
expect(document.documentElement.style.fontSize).toBe('12px');
|
||||
|
||||
atom.config.set(`${themeName}.fontSize`, '10')
|
||||
expect(document.documentElement.style.fontSize).toBe('10px')
|
||||
})
|
||||
atom.config.set(`${themeName}.fontSize`, '10');
|
||||
expect(document.documentElement.style.fontSize).toBe('10px');
|
||||
});
|
||||
|
||||
it('allows the tab sizing to be set via config', () => {
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Maximum')
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Maximum');
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)
|
||||
).toBe('maximum')
|
||||
})
|
||||
).toBe('maximum');
|
||||
});
|
||||
|
||||
it('allows the tab sizing to be set via config', () => {
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Minimum')
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Minimum');
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)
|
||||
).toBe('minimum')
|
||||
})
|
||||
).toBe('minimum');
|
||||
});
|
||||
|
||||
it('allows the tab close button to be shown on the left via config', () => {
|
||||
atom.config.set(`${themeName}.tabCloseButton`, 'Left')
|
||||
atom.config.set(`${themeName}.tabCloseButton`, 'Left');
|
||||
expect(
|
||||
document.documentElement.getAttribute(
|
||||
`theme-${themeName}-tab-close-button`
|
||||
)
|
||||
).toBe('left')
|
||||
})
|
||||
).toBe('left');
|
||||
});
|
||||
|
||||
it('allows the dock toggle buttons to be hidden via config', () => {
|
||||
atom.config.set(`${themeName}.hideDockButtons`, true)
|
||||
atom.config.set(`${themeName}.hideDockButtons`, true);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`)
|
||||
).toBe('hidden')
|
||||
})
|
||||
).toBe('hidden');
|
||||
});
|
||||
|
||||
it('allows the tree-view headers to be sticky via config', () => {
|
||||
atom.config.set(`${themeName}.stickyHeaders`, true)
|
||||
atom.config.set(`${themeName}.stickyHeaders`, true);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)
|
||||
).toBe('sticky')
|
||||
})
|
||||
).toBe('sticky');
|
||||
});
|
||||
|
||||
it('allows the tree-view headers to not be sticky via config', () => {
|
||||
atom.config.set(`${themeName}.stickyHeaders`, false)
|
||||
atom.config.set(`${themeName}.stickyHeaders`, false);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)
|
||||
).toBe(null)
|
||||
})
|
||||
})
|
||||
).toBe(null);
|
||||
});
|
||||
});
|
||||
|
@ -1,88 +1,88 @@
|
||||
const root = document.documentElement
|
||||
const themeName = 'one-light-ui'
|
||||
const root = document.documentElement;
|
||||
const themeName = 'one-light-ui';
|
||||
|
||||
module.exports = {
|
||||
activate (state) {
|
||||
atom.config.observe(`${themeName}.fontSize`, setFontSize)
|
||||
atom.config.observe(`${themeName}.tabSizing`, setTabSizing)
|
||||
atom.config.observe(`${themeName}.tabCloseButton`, setTabCloseButton)
|
||||
atom.config.observe(`${themeName}.hideDockButtons`, setHideDockButtons)
|
||||
atom.config.observe(`${themeName}.stickyHeaders`, setStickyHeaders)
|
||||
activate(state) {
|
||||
atom.config.observe(`${themeName}.fontSize`, setFontSize);
|
||||
atom.config.observe(`${themeName}.tabSizing`, setTabSizing);
|
||||
atom.config.observe(`${themeName}.tabCloseButton`, setTabCloseButton);
|
||||
atom.config.observe(`${themeName}.hideDockButtons`, setHideDockButtons);
|
||||
atom.config.observe(`${themeName}.stickyHeaders`, setStickyHeaders);
|
||||
|
||||
// DEPRECATED: This can be removed at some point (added in Atom 1.17/1.18ish)
|
||||
// It removes `layoutMode`
|
||||
if (atom.config.get(`${themeName}.layoutMode`)) {
|
||||
atom.config.unset(`${themeName}.layoutMode`)
|
||||
atom.config.unset(`${themeName}.layoutMode`);
|
||||
}
|
||||
},
|
||||
|
||||
deactivate () {
|
||||
unsetFontSize()
|
||||
unsetTabSizing()
|
||||
unsetTabCloseButton()
|
||||
unsetHideDockButtons()
|
||||
unsetStickyHeaders()
|
||||
deactivate() {
|
||||
unsetFontSize();
|
||||
unsetTabSizing();
|
||||
unsetTabCloseButton();
|
||||
unsetHideDockButtons();
|
||||
unsetStickyHeaders();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Font Size -----------------------
|
||||
|
||||
function setFontSize (currentFontSize) {
|
||||
root.style.fontSize = `${currentFontSize}px`
|
||||
function setFontSize(currentFontSize) {
|
||||
root.style.fontSize = `${currentFontSize}px`;
|
||||
}
|
||||
|
||||
function unsetFontSize () {
|
||||
root.style.fontSize = ''
|
||||
function unsetFontSize() {
|
||||
root.style.fontSize = '';
|
||||
}
|
||||
|
||||
// Tab Sizing -----------------------
|
||||
|
||||
function setTabSizing (tabSizing) {
|
||||
root.setAttribute(`theme-${themeName}-tabsizing`, tabSizing.toLowerCase())
|
||||
function setTabSizing(tabSizing) {
|
||||
root.setAttribute(`theme-${themeName}-tabsizing`, tabSizing.toLowerCase());
|
||||
}
|
||||
|
||||
function unsetTabSizing () {
|
||||
root.removeAttribute(`theme-${themeName}-tabsizing`)
|
||||
function unsetTabSizing() {
|
||||
root.removeAttribute(`theme-${themeName}-tabsizing`);
|
||||
}
|
||||
|
||||
// Tab Close Button -----------------------
|
||||
|
||||
function setTabCloseButton (tabCloseButton) {
|
||||
function setTabCloseButton(tabCloseButton) {
|
||||
if (tabCloseButton === 'Left') {
|
||||
root.setAttribute(`theme-${themeName}-tab-close-button`, 'left')
|
||||
root.setAttribute(`theme-${themeName}-tab-close-button`, 'left');
|
||||
} else {
|
||||
unsetTabCloseButton()
|
||||
unsetTabCloseButton();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetTabCloseButton () {
|
||||
root.removeAttribute(`theme-${themeName}-tab-close-button`)
|
||||
function unsetTabCloseButton() {
|
||||
root.removeAttribute(`theme-${themeName}-tab-close-button`);
|
||||
}
|
||||
|
||||
// Dock Buttons -----------------------
|
||||
|
||||
function setHideDockButtons (hideDockButtons) {
|
||||
function setHideDockButtons(hideDockButtons) {
|
||||
if (hideDockButtons) {
|
||||
root.setAttribute(`theme-${themeName}-dock-buttons`, 'hidden')
|
||||
root.setAttribute(`theme-${themeName}-dock-buttons`, 'hidden');
|
||||
} else {
|
||||
unsetHideDockButtons()
|
||||
unsetHideDockButtons();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetHideDockButtons () {
|
||||
root.removeAttribute(`theme-${themeName}-dock-buttons`)
|
||||
function unsetHideDockButtons() {
|
||||
root.removeAttribute(`theme-${themeName}-dock-buttons`);
|
||||
}
|
||||
|
||||
// Sticky Headers -----------------------
|
||||
|
||||
function setStickyHeaders (stickyHeaders) {
|
||||
function setStickyHeaders(stickyHeaders) {
|
||||
if (stickyHeaders) {
|
||||
root.setAttribute(`theme-${themeName}-sticky-headers`, 'sticky')
|
||||
root.setAttribute(`theme-${themeName}-sticky-headers`, 'sticky');
|
||||
} else {
|
||||
unsetStickyHeaders()
|
||||
unsetStickyHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
function unsetStickyHeaders () {
|
||||
root.removeAttribute(`theme-${themeName}-sticky-headers`)
|
||||
function unsetStickyHeaders() {
|
||||
root.removeAttribute(`theme-${themeName}-sticky-headers`);
|
||||
}
|
||||
|
@ -1,58 +1,58 @@
|
||||
const themeName = 'one-light-ui'
|
||||
const themeName = 'one-light-ui';
|
||||
|
||||
describe(`${themeName} theme`, () => {
|
||||
beforeEach(() => {
|
||||
waitsForPromise(() => atom.packages.activatePackage(themeName))
|
||||
})
|
||||
waitsForPromise(() => atom.packages.activatePackage(themeName));
|
||||
});
|
||||
|
||||
it('allows the font size to be set via config', () => {
|
||||
expect(document.documentElement.style.fontSize).toBe('12px')
|
||||
expect(document.documentElement.style.fontSize).toBe('12px');
|
||||
|
||||
atom.config.set(`${themeName}.fontSize`, '10')
|
||||
expect(document.documentElement.style.fontSize).toBe('10px')
|
||||
})
|
||||
atom.config.set(`${themeName}.fontSize`, '10');
|
||||
expect(document.documentElement.style.fontSize).toBe('10px');
|
||||
});
|
||||
|
||||
it('allows the tab sizing to be set via config', () => {
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Maximum')
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Maximum');
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)
|
||||
).toBe('maximum')
|
||||
})
|
||||
).toBe('maximum');
|
||||
});
|
||||
|
||||
it('allows the tab sizing to be set via config', () => {
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Minimum')
|
||||
atom.config.set(`${themeName}.tabSizing`, 'Minimum');
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-tabsizing`)
|
||||
).toBe('minimum')
|
||||
})
|
||||
).toBe('minimum');
|
||||
});
|
||||
|
||||
it('allows the tab close button to be shown on the left via config', () => {
|
||||
atom.config.set(`${themeName}.tabCloseButton`, 'Left')
|
||||
atom.config.set(`${themeName}.tabCloseButton`, 'Left');
|
||||
expect(
|
||||
document.documentElement.getAttribute(
|
||||
`theme-${themeName}-tab-close-button`
|
||||
)
|
||||
).toBe('left')
|
||||
})
|
||||
).toBe('left');
|
||||
});
|
||||
|
||||
it('allows the dock toggle buttons to be hidden via config', () => {
|
||||
atom.config.set(`${themeName}.hideDockButtons`, true)
|
||||
atom.config.set(`${themeName}.hideDockButtons`, true);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-dock-buttons`)
|
||||
).toBe('hidden')
|
||||
})
|
||||
).toBe('hidden');
|
||||
});
|
||||
|
||||
it('allows the tree-view headers to be sticky via config', () => {
|
||||
atom.config.set(`${themeName}.stickyHeaders`, true)
|
||||
atom.config.set(`${themeName}.stickyHeaders`, true);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)
|
||||
).toBe('sticky')
|
||||
})
|
||||
).toBe('sticky');
|
||||
});
|
||||
|
||||
it('allows the tree-view headers to not be sticky via config', () => {
|
||||
atom.config.set(`${themeName}.stickyHeaders`, false)
|
||||
atom.config.set(`${themeName}.stickyHeaders`, false);
|
||||
expect(
|
||||
document.documentElement.getAttribute(`theme-${themeName}-sticky-headers`)
|
||||
).toBe(null)
|
||||
})
|
||||
})
|
||||
).toBe(null);
|
||||
});
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
var path = require('path');
|
||||
var path = require('path');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var atomCommandPath = path.resolve(__dirname, '..', '..', 'atom.exe');
|
||||
var args = process.argv.slice(2);
|
||||
args.unshift('--executed-from', process.cwd());
|
||||
var options = {detached: true, stdio: 'ignore'};
|
||||
var options = { detached: true, stdio: 'ignore' };
|
||||
spawn(atomCommandPath, args, options);
|
||||
process.exit(0);
|
||||
|
100
script/config.js
100
script/config.js
@ -1,28 +1,31 @@
|
||||
// This module exports paths, names, and other metadata that is referenced
|
||||
// throughout the build.
|
||||
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./lib/spawn-sync')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const spawnSync = require('./lib/spawn-sync');
|
||||
|
||||
const repositoryRootPath = path.resolve(__dirname, '..')
|
||||
const apmRootPath = path.join(repositoryRootPath, 'apm')
|
||||
const scriptRootPath = path.join(repositoryRootPath, 'script')
|
||||
const buildOutputPath = path.join(repositoryRootPath, 'out')
|
||||
const docsOutputPath = path.join(repositoryRootPath, 'docs', 'output')
|
||||
const intermediateAppPath = path.join(buildOutputPath, 'app')
|
||||
const symbolsPath = path.join(buildOutputPath, 'symbols')
|
||||
const electronDownloadPath = path.join(repositoryRootPath, 'electron')
|
||||
const homeDirPath = process.env.HOME || process.env.USERPROFILE
|
||||
const atomHomeDirPath = process.env.ATOM_HOME || path.join(homeDirPath, '.atom')
|
||||
const repositoryRootPath = path.resolve(__dirname, '..');
|
||||
const apmRootPath = path.join(repositoryRootPath, 'apm');
|
||||
const scriptRootPath = path.join(repositoryRootPath, 'script');
|
||||
const buildOutputPath = path.join(repositoryRootPath, 'out');
|
||||
const docsOutputPath = path.join(repositoryRootPath, 'docs', 'output');
|
||||
const intermediateAppPath = path.join(buildOutputPath, 'app');
|
||||
const symbolsPath = path.join(buildOutputPath, 'symbols');
|
||||
const electronDownloadPath = path.join(repositoryRootPath, 'electron');
|
||||
const homeDirPath = process.env.HOME || process.env.USERPROFILE;
|
||||
const atomHomeDirPath =
|
||||
process.env.ATOM_HOME || path.join(homeDirPath, '.atom');
|
||||
|
||||
const appMetadata = require(path.join(repositoryRootPath, 'package.json'))
|
||||
const apmMetadata = require(path.join(apmRootPath, 'package.json'))
|
||||
const computedAppVersion = computeAppVersion(process.env.ATOM_RELEASE_VERSION || appMetadata.version)
|
||||
const channel = getChannel(computedAppVersion)
|
||||
const appName = getAppName(channel)
|
||||
const appMetadata = require(path.join(repositoryRootPath, 'package.json'));
|
||||
const apmMetadata = require(path.join(apmRootPath, 'package.json'));
|
||||
const computedAppVersion = computeAppVersion(
|
||||
process.env.ATOM_RELEASE_VERSION || appMetadata.version
|
||||
);
|
||||
const channel = getChannel(computedAppVersion);
|
||||
const appName = getAppName(channel);
|
||||
|
||||
module.exports = {
|
||||
appMetadata,
|
||||
@ -43,43 +46,60 @@ module.exports = {
|
||||
getApmBinPath,
|
||||
getNpmBinPath,
|
||||
snapshotAuxiliaryData: {}
|
||||
}
|
||||
};
|
||||
|
||||
function getChannel (version) {
|
||||
const match = version.match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/)
|
||||
function getChannel(version) {
|
||||
const match = version.match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/);
|
||||
if (!match) {
|
||||
throw new Error(`Found incorrectly formatted Atom version ${version}`)
|
||||
throw new Error(`Found incorrectly formatted Atom version ${version}`);
|
||||
} else if (match[2]) {
|
||||
return match[2]
|
||||
return match[2];
|
||||
}
|
||||
|
||||
return 'stable'
|
||||
return 'stable';
|
||||
}
|
||||
|
||||
function getAppName (channel) {
|
||||
function getAppName(channel) {
|
||||
return channel === 'stable'
|
||||
? 'Atom'
|
||||
: `Atom ${process.env.ATOM_CHANNEL_DISPLAY_NAME || channel.charAt(0).toUpperCase() + channel.slice(1)}`
|
||||
: `Atom ${process.env.ATOM_CHANNEL_DISPLAY_NAME ||
|
||||
channel.charAt(0).toUpperCase() + channel.slice(1)}`;
|
||||
}
|
||||
|
||||
function computeAppVersion (version) {
|
||||
function computeAppVersion(version) {
|
||||
if (version.match(/-dev$/)) {
|
||||
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: repositoryRootPath})
|
||||
const commitHash = result.stdout.toString().trim()
|
||||
version += '-' + commitHash
|
||||
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
|
||||
cwd: repositoryRootPath
|
||||
});
|
||||
const commitHash = result.stdout.toString().trim();
|
||||
version += '-' + commitHash;
|
||||
}
|
||||
return version
|
||||
return version;
|
||||
}
|
||||
|
||||
function getApmBinPath () {
|
||||
const apmBinName = process.platform === 'win32' ? 'apm.cmd' : 'apm'
|
||||
return path.join(apmRootPath, 'node_modules', 'atom-package-manager', 'bin', apmBinName)
|
||||
function getApmBinPath() {
|
||||
const apmBinName = process.platform === 'win32' ? 'apm.cmd' : 'apm';
|
||||
return path.join(
|
||||
apmRootPath,
|
||||
'node_modules',
|
||||
'atom-package-manager',
|
||||
'bin',
|
||||
apmBinName
|
||||
);
|
||||
}
|
||||
|
||||
function getNpmBinPath (external = false) {
|
||||
if (process.env.NPM_BIN_PATH) return process.env.NPM_BIN_PATH
|
||||
function getNpmBinPath(external = false) {
|
||||
if (process.env.NPM_BIN_PATH) return process.env.NPM_BIN_PATH;
|
||||
|
||||
const npmBinName = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
||||
const localNpmBinPath = path.resolve(repositoryRootPath, 'script', 'node_modules', '.bin', npmBinName)
|
||||
return !external && fs.existsSync(localNpmBinPath) ? localNpmBinPath : npmBinName
|
||||
const npmBinName = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const localNpmBinPath = path.resolve(
|
||||
repositoryRootPath,
|
||||
'script',
|
||||
'node_modules',
|
||||
'.bin',
|
||||
npmBinName
|
||||
);
|
||||
return !external && fs.existsSync(localNpmBinPath)
|
||||
? localNpmBinPath
|
||||
: npmBinName;
|
||||
}
|
||||
|
@ -1,41 +1,54 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (packagePath) {
|
||||
const nodeModulesPath = path.join(packagePath, 'node_modules')
|
||||
const nodeModulesBackupPath = path.join(packagePath, 'node_modules.bak')
|
||||
module.exports = function(packagePath) {
|
||||
const nodeModulesPath = path.join(packagePath, 'node_modules');
|
||||
const nodeModulesBackupPath = path.join(packagePath, 'node_modules.bak');
|
||||
|
||||
if (fs.existsSync(nodeModulesBackupPath)) {
|
||||
throw new Error('Cannot back up ' + nodeModulesPath + '; ' + nodeModulesBackupPath + ' already exists')
|
||||
throw new Error(
|
||||
'Cannot back up ' +
|
||||
nodeModulesPath +
|
||||
'; ' +
|
||||
nodeModulesBackupPath +
|
||||
' already exists'
|
||||
);
|
||||
}
|
||||
|
||||
// some packages may have no node_modules after deduping, but we still want
|
||||
// to "back-up" and later restore that fact
|
||||
if (!fs.existsSync(nodeModulesPath)) {
|
||||
const msg = 'Skipping backing up ' + nodeModulesPath + ' as it does not exist'
|
||||
console.log(msg.gray)
|
||||
const msg =
|
||||
'Skipping backing up ' + nodeModulesPath + ' as it does not exist';
|
||||
console.log(msg.gray);
|
||||
|
||||
const restore = function stubRestoreNodeModules () {
|
||||
const restore = function stubRestoreNodeModules() {
|
||||
if (fs.existsSync(nodeModulesPath)) {
|
||||
fs.removeSync(nodeModulesPath)
|
||||
fs.removeSync(nodeModulesPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {restore, nodeModulesPath, nodeModulesBackupPath}
|
||||
return { restore, nodeModulesPath, nodeModulesBackupPath };
|
||||
}
|
||||
|
||||
fs.copySync(nodeModulesPath, nodeModulesBackupPath)
|
||||
fs.copySync(nodeModulesPath, nodeModulesBackupPath);
|
||||
|
||||
const restore = function restoreNodeModules () {
|
||||
const restore = function restoreNodeModules() {
|
||||
if (!fs.existsSync(nodeModulesBackupPath)) {
|
||||
throw new Error('Cannot restore ' + nodeModulesPath + '; ' + nodeModulesBackupPath + ' does not exist')
|
||||
throw new Error(
|
||||
'Cannot restore ' +
|
||||
nodeModulesPath +
|
||||
'; ' +
|
||||
nodeModulesBackupPath +
|
||||
' does not exist'
|
||||
);
|
||||
}
|
||||
|
||||
if (fs.existsSync(nodeModulesPath)) {
|
||||
fs.removeSync(nodeModulesPath)
|
||||
fs.removeSync(nodeModulesPath);
|
||||
}
|
||||
fs.renameSync(nodeModulesBackupPath, nodeModulesPath)
|
||||
}
|
||||
fs.renameSync(nodeModulesBackupPath, nodeModulesPath);
|
||||
};
|
||||
|
||||
return {restore, nodeModulesPath, nodeModulesBackupPath}
|
||||
}
|
||||
return { restore, nodeModulesPath, nodeModulesBackupPath };
|
||||
};
|
||||
|
@ -1,33 +1,45 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const buildMetadata = require('../package.json')
|
||||
const CONFIG = require('../config')
|
||||
const semver = require('semver')
|
||||
const buildMetadata = require('../package.json');
|
||||
const CONFIG = require('../config');
|
||||
const semver = require('semver');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
// Chromedriver should be specified as ^n.x where n matches the Electron major version
|
||||
const chromedriverVer = buildMetadata.dependencies['electron-chromedriver']
|
||||
const mksnapshotVer = buildMetadata.dependencies['electron-mksnapshot']
|
||||
const chromedriverVer = buildMetadata.dependencies['electron-chromedriver'];
|
||||
const mksnapshotVer = buildMetadata.dependencies['electron-mksnapshot'];
|
||||
|
||||
// Always use caret on electron-chromedriver so that it can pick up the best minor/patch versions
|
||||
if (!chromedriverVer.startsWith('^')) {
|
||||
throw new Error(`electron-chromedriver version in script/package.json should start with a caret to match latest patch version.`)
|
||||
throw new Error(
|
||||
`electron-chromedriver version in script/package.json should start with a caret to match latest patch version.`
|
||||
);
|
||||
}
|
||||
|
||||
if (!mksnapshotVer.startsWith('^')) {
|
||||
throw new Error(`electron-mksnapshot version in script/package.json should start with a caret to match latest patch version.`)
|
||||
throw new Error(
|
||||
`electron-mksnapshot version in script/package.json should start with a caret to match latest patch version.`
|
||||
);
|
||||
}
|
||||
|
||||
const electronVer = CONFIG.appMetadata.electronVersion
|
||||
const electronVer = CONFIG.appMetadata.electronVersion;
|
||||
if (!semver.satisfies(electronVer, chromedriverVer)) {
|
||||
throw new Error(`electron-chromedriver ${chromedriverVer} incompatible with electron ${electronVer}.\n` +
|
||||
'Did you upgrade electron in package.json and forget to upgrade electron-chromedriver in ' +
|
||||
`script/package.json to '~${semver.major(electronVer)}.${semver.minor(electronVer)}' ?`)
|
||||
throw new Error(
|
||||
`electron-chromedriver ${chromedriverVer} incompatible with electron ${electronVer}.\n` +
|
||||
'Did you upgrade electron in package.json and forget to upgrade electron-chromedriver in ' +
|
||||
`script/package.json to '~${semver.major(electronVer)}.${semver.minor(
|
||||
electronVer
|
||||
)}' ?`
|
||||
);
|
||||
}
|
||||
|
||||
if (!semver.satisfies(electronVer, mksnapshotVer)) {
|
||||
throw new Error(`electron-mksnapshot ${mksnapshotVer} incompatible with electron ${electronVer}.\n` +
|
||||
'Did you upgrade electron in package.json and forget to upgrade electron-mksnapshot in ' +
|
||||
`script/package.json to '~${semver.major(electronVer)}.${semver.minor(electronVer)}' ?`)
|
||||
throw new Error(
|
||||
`electron-mksnapshot ${mksnapshotVer} incompatible with electron ${electronVer}.\n` +
|
||||
'Did you upgrade electron in package.json and forget to upgrade electron-mksnapshot in ' +
|
||||
`script/package.json to '~${semver.major(electronVer)}.${semver.minor(
|
||||
electronVer
|
||||
)}' ?`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,12 +1,12 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
const cachePaths = [
|
||||
path.join(CONFIG.repositoryRootPath, 'electron'),
|
||||
path.join(CONFIG.atomHomeDirPath, '.node-gyp'),
|
||||
@ -19,10 +19,10 @@ module.exports = function () {
|
||||
path.join(CONFIG.atomHomeDirPath, 'electron'),
|
||||
path.join(os.tmpdir(), 'atom-build'),
|
||||
path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
]
|
||||
];
|
||||
|
||||
for (let path of cachePaths) {
|
||||
console.log(`Cleaning ${path}`)
|
||||
fs.removeSync(path)
|
||||
console.log(`Cleaning ${path}`);
|
||||
fs.removeSync(path);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,29 +1,42 @@
|
||||
const path = require('path')
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
// We can't require fs-extra or glob if `script/bootstrap` has never been run,
|
||||
// because they are third party modules. This is okay because cleaning
|
||||
// dependencies only makes sense if dependencies have been installed at least
|
||||
// once.
|
||||
const fs = require('fs-extra')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('glob');
|
||||
|
||||
const apmDependenciesPath = path.join(CONFIG.apmRootPath, 'node_modules')
|
||||
console.log(`Cleaning ${apmDependenciesPath}`)
|
||||
fs.removeSync(apmDependenciesPath)
|
||||
const apmDependenciesPath = path.join(CONFIG.apmRootPath, 'node_modules');
|
||||
console.log(`Cleaning ${apmDependenciesPath}`);
|
||||
fs.removeSync(apmDependenciesPath);
|
||||
|
||||
const atomDependenciesPath = path.join(CONFIG.repositoryRootPath, 'node_modules')
|
||||
console.log(`Cleaning ${atomDependenciesPath}`)
|
||||
fs.removeSync(atomDependenciesPath)
|
||||
const atomDependenciesPath = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'node_modules'
|
||||
);
|
||||
console.log(`Cleaning ${atomDependenciesPath}`);
|
||||
fs.removeSync(atomDependenciesPath);
|
||||
|
||||
const scriptDependenciesPath = path.join(CONFIG.scriptRootPath, 'node_modules')
|
||||
console.log(`Cleaning ${scriptDependenciesPath}`)
|
||||
fs.removeSync(scriptDependenciesPath)
|
||||
const scriptDependenciesPath = path.join(
|
||||
CONFIG.scriptRootPath,
|
||||
'node_modules'
|
||||
);
|
||||
console.log(`Cleaning ${scriptDependenciesPath}`);
|
||||
fs.removeSync(scriptDependenciesPath);
|
||||
|
||||
const bundledPackageDependenciesPaths = path.join(CONFIG.repositoryRootPath, 'packages', '**', 'node_modules')
|
||||
for (const bundledPackageDependencyPath of glob.sync(bundledPackageDependenciesPaths)) {
|
||||
fs.removeSync(bundledPackageDependencyPath)
|
||||
const bundledPackageDependenciesPaths = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'packages',
|
||||
'**',
|
||||
'node_modules'
|
||||
);
|
||||
for (const bundledPackageDependencyPath of glob.sync(
|
||||
bundledPackageDependenciesPaths
|
||||
)) {
|
||||
fs.removeSync(bundledPackageDependencyPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
const fs = require('fs-extra')
|
||||
const CONFIG = require('../config')
|
||||
const fs = require('fs-extra');
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
if (fs.existsSync(CONFIG.buildOutputPath)) {
|
||||
console.log(`Cleaning ${CONFIG.buildOutputPath}`)
|
||||
fs.removeSync(CONFIG.buildOutputPath)
|
||||
console.log(`Cleaning ${CONFIG.buildOutputPath}`);
|
||||
fs.removeSync(CONFIG.buildOutputPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,89 +1,143 @@
|
||||
const downloadFileFromGithub = require('./download-file-from-github')
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const downloadFileFromGithub = require('./download-file-from-github');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const spawnSync = require('./spawn-sync');
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
if (!process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL && !process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH) {
|
||||
console.log('Skipping code signing because the ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable is not defined'.gray)
|
||||
return
|
||||
module.exports = function(packagedAppPath) {
|
||||
if (
|
||||
!process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL &&
|
||||
!process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH
|
||||
) {
|
||||
console.log(
|
||||
'Skipping code signing because the ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable is not defined'
|
||||
.gray
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let certPath = process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH
|
||||
let certPath = process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH;
|
||||
if (!certPath) {
|
||||
certPath = path.join(os.tmpdir(), 'mac.p12')
|
||||
downloadFileFromGithub(process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
certPath = path.join(os.tmpdir(), 'mac.p12');
|
||||
downloadFileFromGithub(
|
||||
process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL,
|
||||
certPath
|
||||
);
|
||||
}
|
||||
try {
|
||||
console.log(`Ensuring keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} exists`)
|
||||
console.log(
|
||||
`Ensuring keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} exists`
|
||||
);
|
||||
try {
|
||||
spawnSync('security', [
|
||||
'show-keychain-info',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
spawnSync(
|
||||
'security',
|
||||
['show-keychain-info', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(`Creating keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
|
||||
console.log(
|
||||
`Creating keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`
|
||||
);
|
||||
// The keychain doesn't exist, try to create it
|
||||
spawnSync('security', [
|
||||
'create-keychain',
|
||||
'-p', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
spawnSync(
|
||||
'security',
|
||||
[
|
||||
'create-keychain',
|
||||
'-p',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
|
||||
// List the keychain to "activate" it. Somehow this seems
|
||||
// to be needed otherwise the signing operation fails
|
||||
spawnSync('security', [
|
||||
'list-keychains',
|
||||
'-s', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
spawnSync(
|
||||
'security',
|
||||
['list-keychains', '-s', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
|
||||
// Make sure it doesn't time out before we use it
|
||||
spawnSync('security', [
|
||||
'set-keychain-settings',
|
||||
'-t', '3600',
|
||||
'-u', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
], {stdio: 'inherit'})
|
||||
spawnSync(
|
||||
'security',
|
||||
[
|
||||
'set-keychain-settings',
|
||||
'-t',
|
||||
'3600',
|
||||
'-u',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
|
||||
const unlockArgs = ['unlock-keychain']
|
||||
console.log(
|
||||
`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`
|
||||
);
|
||||
const unlockArgs = ['unlock-keychain'];
|
||||
// For signing on local workstations, password could be entered interactively
|
||||
if (process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD) {
|
||||
unlockArgs.push('-p', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD)
|
||||
unlockArgs.push(
|
||||
'-p',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD
|
||||
);
|
||||
}
|
||||
unlockArgs.push(process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN)
|
||||
spawnSync('security', unlockArgs, {stdio: 'inherit'})
|
||||
unlockArgs.push(process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN);
|
||||
spawnSync('security', unlockArgs, { stdio: 'inherit' });
|
||||
|
||||
console.log(`Importing certificate at ${certPath} into ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} keychain`)
|
||||
console.log(
|
||||
`Importing certificate at ${certPath} into ${
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
} keychain`
|
||||
);
|
||||
spawnSync('security', [
|
||||
'import', certPath,
|
||||
'-P', process.env.ATOM_MAC_CODE_SIGNING_CERT_PASSWORD,
|
||||
'-k', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN,
|
||||
'-T', '/usr/bin/codesign'
|
||||
])
|
||||
'import',
|
||||
certPath,
|
||||
'-P',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_CERT_PASSWORD,
|
||||
'-k',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN,
|
||||
'-T',
|
||||
'/usr/bin/codesign'
|
||||
]);
|
||||
|
||||
console.log('Running incantation to suppress dialog when signing on macOS Sierra')
|
||||
console.log(
|
||||
'Running incantation to suppress dialog when signing on macOS Sierra'
|
||||
);
|
||||
try {
|
||||
spawnSync('security', [
|
||||
'set-key-partition-list', '-S', 'apple-tool:,apple:', '-s',
|
||||
'-k', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
'set-key-partition-list',
|
||||
'-S',
|
||||
'apple-tool:,apple:',
|
||||
'-s',
|
||||
'-k',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
|
||||
])
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log('Incantation failed... maybe this isn\'t Sierra?')
|
||||
console.log("Incantation failed... maybe this isn't Sierra?");
|
||||
}
|
||||
|
||||
console.log(`Code-signing application at ${packagedAppPath}`)
|
||||
spawnSync('codesign', [
|
||||
'--deep', '--force', '--verbose',
|
||||
'--keychain', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN,
|
||||
'--sign', 'Developer ID Application: GitHub', packagedAppPath
|
||||
], {stdio: 'inherit'})
|
||||
console.log(`Code-signing application at ${packagedAppPath}`);
|
||||
spawnSync(
|
||||
'codesign',
|
||||
[
|
||||
'--deep',
|
||||
'--force',
|
||||
'--verbose',
|
||||
'--keychain',
|
||||
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN,
|
||||
'--sign',
|
||||
'Developer ID Application: GitHub',
|
||||
packagedAppPath
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
} finally {
|
||||
if (!process.env.ATOM_MAC_CODE_SIGNING_CERT_PATH) {
|
||||
console.log(`Deleting certificate at ${certPath}`)
|
||||
fs.removeSync(certPath)
|
||||
console.log(`Deleting certificate at ${certPath}`);
|
||||
fs.removeSync(certPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,33 +1,49 @@
|
||||
const downloadFileFromGithub = require('./download-file-from-github')
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const {spawnSync} = require('child_process')
|
||||
const downloadFileFromGithub = require('./download-file-from-github');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
module.exports = function (filesToSign) {
|
||||
if (!process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL && !process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH) {
|
||||
console.log('Skipping code signing because the ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable is not defined'.gray)
|
||||
return
|
||||
module.exports = function(filesToSign) {
|
||||
if (
|
||||
!process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL &&
|
||||
!process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH
|
||||
) {
|
||||
console.log(
|
||||
'Skipping code signing because the ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL environment variable is not defined'
|
||||
.gray
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let certPath = process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH
|
||||
let certPath = process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH;
|
||||
if (!certPath) {
|
||||
certPath = path.join(os.tmpdir(), 'win.p12')
|
||||
downloadFileFromGithub(process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
|
||||
certPath = path.join(os.tmpdir(), 'win.p12');
|
||||
downloadFileFromGithub(
|
||||
process.env.ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL,
|
||||
certPath
|
||||
);
|
||||
}
|
||||
try {
|
||||
for (const fileToSign of filesToSign) {
|
||||
console.log(`Code-signing executable at ${fileToSign}`)
|
||||
signFile(fileToSign)
|
||||
console.log(`Code-signing executable at ${fileToSign}`);
|
||||
signFile(fileToSign);
|
||||
}
|
||||
} finally {
|
||||
if (!process.env.ATOM_WIN_CODE_SIGNING_CERT_PATH) {
|
||||
fs.removeSync(certPath)
|
||||
fs.removeSync(certPath);
|
||||
}
|
||||
}
|
||||
|
||||
function signFile (fileToSign) {
|
||||
const signCommand = path.resolve(__dirname, '..', 'node_modules', 'electron-winstaller', 'vendor', 'signtool.exe')
|
||||
function signFile(fileToSign) {
|
||||
const signCommand = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'node_modules',
|
||||
'electron-winstaller',
|
||||
'vendor',
|
||||
'signtool.exe'
|
||||
);
|
||||
const args = [
|
||||
'sign',
|
||||
`/f ${certPath}`, // Signing cert file
|
||||
@ -36,11 +52,20 @@ module.exports = function (filesToSign) {
|
||||
'/tr http://timestamp.digicert.com', // Time stamp server
|
||||
'/td sha256', // Times stamp algorithm
|
||||
`"${fileToSign}"`
|
||||
]
|
||||
const result = spawnSync(signCommand, args, {stdio: 'inherit', shell: true})
|
||||
];
|
||||
const result = spawnSync(signCommand, args, {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
// Ensure we do not dump the signing password into the logs if something goes wrong
|
||||
throw new Error(`Command ${signCommand} ${args.map(a => a.replace(process.env.ATOM_WIN_CODE_SIGNING_CERT_PASSWORD, '******')).join(' ')} exited with code ${result.status}`)
|
||||
throw new Error(
|
||||
`Command ${signCommand} ${args
|
||||
.map(a =>
|
||||
a.replace(process.env.ATOM_WIN_CODE_SIGNING_CERT_PASSWORD, '******')
|
||||
)
|
||||
.join(' ')} exited with code ${result.status}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,56 +1,67 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const { path7za } = require('7zip-bin')
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const spawnSync = require('./spawn-sync');
|
||||
const { path7za } = require('7zip-bin');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
const appArchivePath = path.join(CONFIG.buildOutputPath, getArchiveName())
|
||||
compress(packagedAppPath, appArchivePath)
|
||||
module.exports = function(packagedAppPath) {
|
||||
const appArchivePath = path.join(CONFIG.buildOutputPath, getArchiveName());
|
||||
compress(packagedAppPath, appArchivePath);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const symbolsArchivePath = path.join(CONFIG.buildOutputPath, 'atom-mac-symbols.zip')
|
||||
compress(CONFIG.symbolsPath, symbolsArchivePath)
|
||||
const symbolsArchivePath = path.join(
|
||||
CONFIG.buildOutputPath,
|
||||
'atom-mac-symbols.zip'
|
||||
);
|
||||
compress(CONFIG.symbolsPath, symbolsArchivePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getArchiveName () {
|
||||
function getArchiveName() {
|
||||
switch (process.platform) {
|
||||
case 'darwin': return 'atom-mac.zip'
|
||||
case 'win32': return `atom-${process.arch === 'x64' ? 'x64-' : ''}windows.zip`
|
||||
default: return `atom-${getLinuxArchiveArch()}.tar.gz`
|
||||
case 'darwin':
|
||||
return 'atom-mac.zip';
|
||||
case 'win32':
|
||||
return `atom-${process.arch === 'x64' ? 'x64-' : ''}windows.zip`;
|
||||
default:
|
||||
return `atom-${getLinuxArchiveArch()}.tar.gz`;
|
||||
}
|
||||
}
|
||||
|
||||
function getLinuxArchiveArch () {
|
||||
function getLinuxArchiveArch() {
|
||||
switch (process.arch) {
|
||||
case 'ia32': return 'i386'
|
||||
case 'x64' : return 'amd64'
|
||||
default: return process.arch
|
||||
case 'ia32':
|
||||
return 'i386';
|
||||
case 'x64':
|
||||
return 'amd64';
|
||||
default:
|
||||
return process.arch;
|
||||
}
|
||||
}
|
||||
|
||||
function compress (inputDirPath, outputArchivePath) {
|
||||
function compress(inputDirPath, outputArchivePath) {
|
||||
if (fs.existsSync(outputArchivePath)) {
|
||||
console.log(`Deleting "${outputArchivePath}"`)
|
||||
fs.removeSync(outputArchivePath)
|
||||
console.log(`Deleting "${outputArchivePath}"`);
|
||||
fs.removeSync(outputArchivePath);
|
||||
}
|
||||
|
||||
console.log(`Compressing "${inputDirPath}" to "${outputArchivePath}"`)
|
||||
let compressCommand, compressArguments
|
||||
console.log(`Compressing "${inputDirPath}" to "${outputArchivePath}"`);
|
||||
let compressCommand, compressArguments;
|
||||
if (process.platform === 'darwin') {
|
||||
compressCommand = 'zip'
|
||||
compressArguments = ['-r', '--symlinks']
|
||||
compressCommand = 'zip';
|
||||
compressArguments = ['-r', '--symlinks'];
|
||||
} else if (process.platform === 'win32') {
|
||||
compressCommand = path7za
|
||||
compressArguments = ['a', '-r']
|
||||
compressCommand = path7za;
|
||||
compressArguments = ['a', '-r'];
|
||||
} else {
|
||||
compressCommand = 'tar'
|
||||
compressArguments = ['caf']
|
||||
compressCommand = 'tar';
|
||||
compressArguments = ['caf'];
|
||||
}
|
||||
compressArguments.push(outputArchivePath, path.basename(inputDirPath))
|
||||
spawnSync(compressCommand, compressArguments, {cwd: path.dirname(inputDirPath)})
|
||||
compressArguments.push(outputArchivePath, path.basename(inputDirPath));
|
||||
spawnSync(compressCommand, compressArguments, {
|
||||
cwd: path.dirname(inputDirPath)
|
||||
});
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
// This module exports a function that copies all the static assets into the
|
||||
// appropriate location in the build output directory.
|
||||
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const CONFIG = require('../config')
|
||||
const glob = require('glob')
|
||||
const includePathInPackagedApp = require('./include-path-in-packaged-app')
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const CONFIG = require('../config');
|
||||
const glob = require('glob');
|
||||
const includePathInPackagedApp = require('./include-path-in-packaged-app');
|
||||
|
||||
module.exports = function () {
|
||||
console.log(`Copying assets to ${CONFIG.intermediateAppPath}`)
|
||||
module.exports = function() {
|
||||
console.log(`Copying assets to ${CONFIG.intermediateAppPath}`);
|
||||
let srcPaths = [
|
||||
path.join(CONFIG.repositoryRootPath, 'benchmarks', 'benchmark-runner.js'),
|
||||
path.join(CONFIG.repositoryRootPath, 'dot-atom'),
|
||||
@ -19,10 +19,16 @@ module.exports = function () {
|
||||
path.join(CONFIG.repositoryRootPath, 'static'),
|
||||
path.join(CONFIG.repositoryRootPath, 'src'),
|
||||
path.join(CONFIG.repositoryRootPath, 'vendor')
|
||||
]
|
||||
srcPaths = srcPaths.concat(glob.sync(path.join(CONFIG.repositoryRootPath, 'spec', '*.*'), {ignore: path.join('**', '*-spec.*')}))
|
||||
];
|
||||
srcPaths = srcPaths.concat(
|
||||
glob.sync(path.join(CONFIG.repositoryRootPath, 'spec', '*.*'), {
|
||||
ignore: path.join('**', '*-spec.*')
|
||||
})
|
||||
);
|
||||
for (let srcPath of srcPaths) {
|
||||
fs.copySync(srcPath, computeDestinationPath(srcPath), {filter: includePathInPackagedApp})
|
||||
fs.copySync(srcPath, computeDestinationPath(srcPath), {
|
||||
filter: includePathInPackagedApp
|
||||
});
|
||||
}
|
||||
|
||||
// Run a copy pass to dereference symlinked directories under node_modules.
|
||||
@ -30,21 +36,37 @@ module.exports = function () {
|
||||
// copied to the output folder correctly. We dereference only the top-level
|
||||
// symlinks and not nested symlinks to avoid issues where symlinked binaries
|
||||
// are duplicated in Atom's installation packages (see atom/atom#18490).
|
||||
const nodeModulesPath = path.join(CONFIG.repositoryRootPath, 'node_modules')
|
||||
glob.sync(path.join(nodeModulesPath, '*'))
|
||||
.map(p => fs.lstatSync(p).isSymbolicLink() ? path.resolve(nodeModulesPath, fs.readlinkSync(p)) : p)
|
||||
.forEach(modulePath => {
|
||||
const destPath = path.join(CONFIG.intermediateAppPath, 'node_modules', path.basename(modulePath))
|
||||
fs.copySync(modulePath, destPath, { filter: includePathInPackagedApp })
|
||||
})
|
||||
const nodeModulesPath = path.join(CONFIG.repositoryRootPath, 'node_modules');
|
||||
glob
|
||||
.sync(path.join(nodeModulesPath, '*'))
|
||||
.map(p =>
|
||||
fs.lstatSync(p).isSymbolicLink()
|
||||
? path.resolve(nodeModulesPath, fs.readlinkSync(p))
|
||||
: p
|
||||
)
|
||||
.forEach(modulePath => {
|
||||
const destPath = path.join(
|
||||
CONFIG.intermediateAppPath,
|
||||
'node_modules',
|
||||
path.basename(modulePath)
|
||||
);
|
||||
fs.copySync(modulePath, destPath, { filter: includePathInPackagedApp });
|
||||
});
|
||||
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png', '1024.png'),
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'app-icons',
|
||||
CONFIG.channel,
|
||||
'png',
|
||||
'1024.png'
|
||||
),
|
||||
path.join(CONFIG.intermediateAppPath, 'resources', 'atom.png')
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function computeDestinationPath (srcPath) {
|
||||
const relativePath = path.relative(CONFIG.repositoryRootPath, srcPath)
|
||||
return path.join(CONFIG.intermediateAppPath, relativePath)
|
||||
function computeDestinationPath(srcPath) {
|
||||
const relativePath = path.relative(CONFIG.repositoryRootPath, srcPath);
|
||||
return path.join(CONFIG.intermediateAppPath, relativePath);
|
||||
}
|
||||
|
@ -1,127 +1,224 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const template = require('lodash.template')
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const spawnSync = require('./spawn-sync');
|
||||
const template = require('lodash.template');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
console.log(`Creating Debian package for "${packagedAppPath}"`)
|
||||
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
|
||||
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
|
||||
const appDescription = CONFIG.appMetadata.description
|
||||
const appVersion = CONFIG.appMetadata.version
|
||||
let arch
|
||||
module.exports = function(packagedAppPath) {
|
||||
console.log(`Creating Debian package for "${packagedAppPath}"`);
|
||||
const atomExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`;
|
||||
const apmExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`;
|
||||
const appDescription = CONFIG.appMetadata.description;
|
||||
const appVersion = CONFIG.appMetadata.version;
|
||||
let arch;
|
||||
if (process.arch === 'ia32') {
|
||||
arch = 'i386'
|
||||
arch = 'i386';
|
||||
} else if (process.arch === 'x64') {
|
||||
arch = 'amd64'
|
||||
arch = 'amd64';
|
||||
} else if (process.arch === 'ppc') {
|
||||
arch = 'powerpc'
|
||||
arch = 'powerpc';
|
||||
} else {
|
||||
arch = process.arch
|
||||
arch = process.arch;
|
||||
}
|
||||
|
||||
const outputDebianPackageFilePath = path.join(CONFIG.buildOutputPath, `atom-${arch}.deb`)
|
||||
const debianPackageDirPath = path.join(os.tmpdir(), path.basename(packagedAppPath))
|
||||
const debianPackageConfigPath = path.join(debianPackageDirPath, 'DEBIAN')
|
||||
const debianPackageInstallDirPath = path.join(debianPackageDirPath, 'usr')
|
||||
const debianPackageBinDirPath = path.join(debianPackageInstallDirPath, 'bin')
|
||||
const debianPackageShareDirPath = path.join(debianPackageInstallDirPath, 'share')
|
||||
const debianPackageAtomDirPath = path.join(debianPackageShareDirPath, atomExecutableName)
|
||||
const debianPackageApplicationsDirPath = path.join(debianPackageShareDirPath, 'applications')
|
||||
const debianPackageIconsDirPath = path.join(debianPackageShareDirPath, 'pixmaps')
|
||||
const debianPackageLintianOverridesDirPath = path.join(debianPackageShareDirPath, 'lintian', 'overrides')
|
||||
const debianPackageDocsDirPath = path.join(debianPackageShareDirPath, 'doc', atomExecutableName)
|
||||
const outputDebianPackageFilePath = path.join(
|
||||
CONFIG.buildOutputPath,
|
||||
`atom-${arch}.deb`
|
||||
);
|
||||
const debianPackageDirPath = path.join(
|
||||
os.tmpdir(),
|
||||
path.basename(packagedAppPath)
|
||||
);
|
||||
const debianPackageConfigPath = path.join(debianPackageDirPath, 'DEBIAN');
|
||||
const debianPackageInstallDirPath = path.join(debianPackageDirPath, 'usr');
|
||||
const debianPackageBinDirPath = path.join(debianPackageInstallDirPath, 'bin');
|
||||
const debianPackageShareDirPath = path.join(
|
||||
debianPackageInstallDirPath,
|
||||
'share'
|
||||
);
|
||||
const debianPackageAtomDirPath = path.join(
|
||||
debianPackageShareDirPath,
|
||||
atomExecutableName
|
||||
);
|
||||
const debianPackageApplicationsDirPath = path.join(
|
||||
debianPackageShareDirPath,
|
||||
'applications'
|
||||
);
|
||||
const debianPackageIconsDirPath = path.join(
|
||||
debianPackageShareDirPath,
|
||||
'pixmaps'
|
||||
);
|
||||
const debianPackageLintianOverridesDirPath = path.join(
|
||||
debianPackageShareDirPath,
|
||||
'lintian',
|
||||
'overrides'
|
||||
);
|
||||
const debianPackageDocsDirPath = path.join(
|
||||
debianPackageShareDirPath,
|
||||
'doc',
|
||||
atomExecutableName
|
||||
);
|
||||
|
||||
if (fs.existsSync(debianPackageDirPath)) {
|
||||
console.log(`Deleting existing build dir for Debian package at "${debianPackageDirPath}"`)
|
||||
fs.removeSync(debianPackageDirPath)
|
||||
console.log(
|
||||
`Deleting existing build dir for Debian package at "${debianPackageDirPath}"`
|
||||
);
|
||||
fs.removeSync(debianPackageDirPath);
|
||||
}
|
||||
if (fs.existsSync(`${debianPackageDirPath}.deb`)) {
|
||||
console.log(`Deleting existing Debian package at "${debianPackageDirPath}.deb"`)
|
||||
fs.removeSync(`${debianPackageDirPath}.deb`)
|
||||
console.log(
|
||||
`Deleting existing Debian package at "${debianPackageDirPath}.deb"`
|
||||
);
|
||||
fs.removeSync(`${debianPackageDirPath}.deb`);
|
||||
}
|
||||
if (fs.existsSync(debianPackageDirPath)) {
|
||||
console.log(`Deleting existing Debian package at "${outputDebianPackageFilePath}"`)
|
||||
fs.removeSync(debianPackageDirPath)
|
||||
console.log(
|
||||
`Deleting existing Debian package at "${outputDebianPackageFilePath}"`
|
||||
);
|
||||
fs.removeSync(debianPackageDirPath);
|
||||
}
|
||||
|
||||
console.log(`Creating Debian package directory structure at "${debianPackageDirPath}"`)
|
||||
fs.mkdirpSync(debianPackageDirPath)
|
||||
fs.mkdirpSync(debianPackageConfigPath)
|
||||
fs.mkdirpSync(debianPackageInstallDirPath)
|
||||
fs.mkdirpSync(debianPackageShareDirPath)
|
||||
fs.mkdirpSync(debianPackageApplicationsDirPath)
|
||||
fs.mkdirpSync(debianPackageIconsDirPath)
|
||||
fs.mkdirpSync(debianPackageLintianOverridesDirPath)
|
||||
fs.mkdirpSync(debianPackageDocsDirPath)
|
||||
fs.mkdirpSync(debianPackageBinDirPath)
|
||||
console.log(
|
||||
`Creating Debian package directory structure at "${debianPackageDirPath}"`
|
||||
);
|
||||
fs.mkdirpSync(debianPackageDirPath);
|
||||
fs.mkdirpSync(debianPackageConfigPath);
|
||||
fs.mkdirpSync(debianPackageInstallDirPath);
|
||||
fs.mkdirpSync(debianPackageShareDirPath);
|
||||
fs.mkdirpSync(debianPackageApplicationsDirPath);
|
||||
fs.mkdirpSync(debianPackageIconsDirPath);
|
||||
fs.mkdirpSync(debianPackageLintianOverridesDirPath);
|
||||
fs.mkdirpSync(debianPackageDocsDirPath);
|
||||
fs.mkdirpSync(debianPackageBinDirPath);
|
||||
|
||||
console.log(`Copying "${packagedAppPath}" to "${debianPackageAtomDirPath}"`)
|
||||
fs.copySync(packagedAppPath, debianPackageAtomDirPath)
|
||||
fs.chmodSync(debianPackageAtomDirPath, '755')
|
||||
console.log(`Copying "${packagedAppPath}" to "${debianPackageAtomDirPath}"`);
|
||||
fs.copySync(packagedAppPath, debianPackageAtomDirPath);
|
||||
fs.chmodSync(debianPackageAtomDirPath, '755');
|
||||
|
||||
console.log(`Copying binaries into "${debianPackageBinDirPath}"`)
|
||||
fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), path.join(debianPackageBinDirPath, atomExecutableName))
|
||||
console.log(`Copying binaries into "${debianPackageBinDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'atom.sh'),
|
||||
path.join(debianPackageBinDirPath, atomExecutableName)
|
||||
);
|
||||
fs.symlinkSync(
|
||||
path.join('..', 'share', atomExecutableName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'),
|
||||
path.join(
|
||||
'..',
|
||||
'share',
|
||||
atomExecutableName,
|
||||
'resources',
|
||||
'app',
|
||||
'apm',
|
||||
'node_modules',
|
||||
'.bin',
|
||||
'apm'
|
||||
),
|
||||
path.join(debianPackageBinDirPath, apmExecutableName)
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Writing control file into "${debianPackageConfigPath}"`)
|
||||
const packageSizeInKilobytes = spawnSync('du', ['-sk', packagedAppPath]).stdout.toString().split(/\s+/)[0]
|
||||
const controlFileTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'debian', 'control.in'))
|
||||
console.log(`Writing control file into "${debianPackageConfigPath}"`);
|
||||
const packageSizeInKilobytes = spawnSync('du', ['-sk', packagedAppPath])
|
||||
.stdout.toString()
|
||||
.split(/\s+/)[0];
|
||||
const controlFileTemplate = fs.readFileSync(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'debian',
|
||||
'control.in'
|
||||
)
|
||||
);
|
||||
const controlFileContents = template(controlFileTemplate)({
|
||||
appFileName: atomExecutableName,
|
||||
version: appVersion,
|
||||
arch: arch,
|
||||
installedSize: packageSizeInKilobytes,
|
||||
description: appDescription
|
||||
})
|
||||
fs.writeFileSync(path.join(debianPackageConfigPath, 'control'), controlFileContents)
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(debianPackageConfigPath, 'control'),
|
||||
controlFileContents
|
||||
);
|
||||
|
||||
console.log(`Writing desktop entry file into "${debianPackageApplicationsDirPath}"`)
|
||||
const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in'))
|
||||
console.log(
|
||||
`Writing desktop entry file into "${debianPackageApplicationsDirPath}"`
|
||||
);
|
||||
const desktopEntryTemplate = fs.readFileSync(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'atom.desktop.in'
|
||||
)
|
||||
);
|
||||
const desktopEntryContents = template(desktopEntryTemplate)({
|
||||
appName: CONFIG.appName,
|
||||
appFileName: atomExecutableName,
|
||||
description: appDescription,
|
||||
installDir: '/usr',
|
||||
iconPath: atomExecutableName
|
||||
})
|
||||
fs.writeFileSync(path.join(debianPackageApplicationsDirPath, `${atomExecutableName}.desktop`), desktopEntryContents)
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(
|
||||
debianPackageApplicationsDirPath,
|
||||
`${atomExecutableName}.desktop`
|
||||
),
|
||||
desktopEntryContents
|
||||
);
|
||||
|
||||
console.log(`Copying icon into "${debianPackageIconsDirPath}"`)
|
||||
console.log(`Copying icon into "${debianPackageIconsDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(packagedAppPath, 'resources', 'app.asar.unpacked', 'resources', 'atom.png'),
|
||||
path.join(
|
||||
packagedAppPath,
|
||||
'resources',
|
||||
'app.asar.unpacked',
|
||||
'resources',
|
||||
'atom.png'
|
||||
),
|
||||
path.join(debianPackageIconsDirPath, `${atomExecutableName}.png`)
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Copying license into "${debianPackageDocsDirPath}"`)
|
||||
console.log(`Copying license into "${debianPackageDocsDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(packagedAppPath, 'resources', 'LICENSE.md'),
|
||||
path.join(debianPackageDocsDirPath, 'copyright')
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Copying lintian overrides into "${debianPackageLintianOverridesDirPath}"`)
|
||||
console.log(
|
||||
`Copying lintian overrides into "${debianPackageLintianOverridesDirPath}"`
|
||||
);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'debian', 'lintian-overrides'),
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'debian',
|
||||
'lintian-overrides'
|
||||
),
|
||||
path.join(debianPackageLintianOverridesDirPath, atomExecutableName)
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Copying polkit configuration into "${debianPackageShareDirPath}"`)
|
||||
console.log(
|
||||
`Copying polkit configuration into "${debianPackageShareDirPath}"`
|
||||
);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.policy'),
|
||||
path.join(debianPackageShareDirPath, 'polkit-1', 'actions', 'atom.policy')
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Generating .deb file from ${debianPackageDirPath}`)
|
||||
spawnSync('fakeroot', ['dpkg-deb', '-b', debianPackageDirPath], {stdio: 'inherit'})
|
||||
console.log(`Generating .deb file from ${debianPackageDirPath}`);
|
||||
spawnSync('fakeroot', ['dpkg-deb', '-b', debianPackageDirPath], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
console.log(`Copying generated package into "${outputDebianPackageFilePath}"`)
|
||||
fs.copySync(`${debianPackageDirPath}.deb`, outputDebianPackageFilePath)
|
||||
}
|
||||
console.log(
|
||||
`Copying generated package into "${outputDebianPackageFilePath}"`
|
||||
);
|
||||
fs.copySync(`${debianPackageDirPath}.deb`, outputDebianPackageFilePath);
|
||||
};
|
||||
|
@ -1,54 +1,79 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const spawnSync = require('./spawn-sync')
|
||||
const template = require('lodash.template')
|
||||
const assert = require('assert');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const spawnSync = require('./spawn-sync');
|
||||
const template = require('lodash.template');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
console.log(`Creating rpm package for "${packagedAppPath}"`)
|
||||
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
|
||||
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
|
||||
const appName = CONFIG.appName
|
||||
const appDescription = CONFIG.appMetadata.description
|
||||
module.exports = function(packagedAppPath) {
|
||||
console.log(`Creating rpm package for "${packagedAppPath}"`);
|
||||
const atomExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`;
|
||||
const apmExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`;
|
||||
const appName = CONFIG.appName;
|
||||
const appDescription = CONFIG.appMetadata.description;
|
||||
// RPM versions can't have dashes or tildes in them.
|
||||
// (Ref.: https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning)
|
||||
const appVersion = CONFIG.appMetadata.version.replace(/-/g, '.')
|
||||
const appVersion = CONFIG.appMetadata.version.replace(/-/g, '.');
|
||||
|
||||
const rpmPackageDirPath = path.join(CONFIG.homeDirPath, 'rpmbuild')
|
||||
const rpmPackageBuildDirPath = path.join(rpmPackageDirPath, 'BUILD')
|
||||
const rpmPackageSourcesDirPath = path.join(rpmPackageDirPath, 'SOURCES')
|
||||
const rpmPackageSpecsDirPath = path.join(rpmPackageDirPath, 'SPECS')
|
||||
const rpmPackageRpmsDirPath = path.join(rpmPackageDirPath, 'RPMS')
|
||||
const rpmPackageApplicationDirPath = path.join(rpmPackageBuildDirPath, appName)
|
||||
const rpmPackageIconsDirPath = path.join(rpmPackageBuildDirPath, 'icons')
|
||||
const rpmPackageDirPath = path.join(CONFIG.homeDirPath, 'rpmbuild');
|
||||
const rpmPackageBuildDirPath = path.join(rpmPackageDirPath, 'BUILD');
|
||||
const rpmPackageSourcesDirPath = path.join(rpmPackageDirPath, 'SOURCES');
|
||||
const rpmPackageSpecsDirPath = path.join(rpmPackageDirPath, 'SPECS');
|
||||
const rpmPackageRpmsDirPath = path.join(rpmPackageDirPath, 'RPMS');
|
||||
const rpmPackageApplicationDirPath = path.join(
|
||||
rpmPackageBuildDirPath,
|
||||
appName
|
||||
);
|
||||
const rpmPackageIconsDirPath = path.join(rpmPackageBuildDirPath, 'icons');
|
||||
|
||||
if (fs.existsSync(rpmPackageDirPath)) {
|
||||
console.log(`Deleting existing rpm build directory at "${rpmPackageDirPath}"`)
|
||||
fs.removeSync(rpmPackageDirPath)
|
||||
console.log(
|
||||
`Deleting existing rpm build directory at "${rpmPackageDirPath}"`
|
||||
);
|
||||
fs.removeSync(rpmPackageDirPath);
|
||||
}
|
||||
|
||||
console.log(`Creating rpm package directory structure at "${rpmPackageDirPath}"`)
|
||||
fs.mkdirpSync(rpmPackageDirPath)
|
||||
fs.mkdirpSync(rpmPackageBuildDirPath)
|
||||
fs.mkdirpSync(rpmPackageSourcesDirPath)
|
||||
fs.mkdirpSync(rpmPackageSpecsDirPath)
|
||||
console.log(
|
||||
`Creating rpm package directory structure at "${rpmPackageDirPath}"`
|
||||
);
|
||||
fs.mkdirpSync(rpmPackageDirPath);
|
||||
fs.mkdirpSync(rpmPackageBuildDirPath);
|
||||
fs.mkdirpSync(rpmPackageSourcesDirPath);
|
||||
fs.mkdirpSync(rpmPackageSpecsDirPath);
|
||||
|
||||
console.log(`Copying "${packagedAppPath}" to "${rpmPackageApplicationDirPath}"`)
|
||||
fs.copySync(packagedAppPath, rpmPackageApplicationDirPath)
|
||||
console.log(
|
||||
`Copying "${packagedAppPath}" to "${rpmPackageApplicationDirPath}"`
|
||||
);
|
||||
fs.copySync(packagedAppPath, rpmPackageApplicationDirPath);
|
||||
|
||||
console.log(`Copying icons into "${rpmPackageIconsDirPath}"`)
|
||||
console.log(`Copying icons into "${rpmPackageIconsDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png'),
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'app-icons',
|
||||
CONFIG.channel,
|
||||
'png'
|
||||
),
|
||||
rpmPackageIconsDirPath
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Writing rpm package spec file into "${rpmPackageSpecsDirPath}"`)
|
||||
const rpmPackageSpecFilePath = path.join(rpmPackageSpecsDirPath, 'atom.spec')
|
||||
const rpmPackageSpecsTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'redhat', 'atom.spec.in'))
|
||||
console.log(`Writing rpm package spec file into "${rpmPackageSpecsDirPath}"`);
|
||||
const rpmPackageSpecFilePath = path.join(rpmPackageSpecsDirPath, 'atom.spec');
|
||||
const rpmPackageSpecsTemplate = fs.readFileSync(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'redhat',
|
||||
'atom.spec.in'
|
||||
)
|
||||
);
|
||||
const rpmPackageSpecsContents = template(rpmPackageSpecsTemplate)({
|
||||
appName: appName,
|
||||
appFileName: atomExecutableName,
|
||||
@ -56,41 +81,65 @@ module.exports = function (packagedAppPath) {
|
||||
description: appDescription,
|
||||
installDir: '/usr',
|
||||
version: appVersion
|
||||
})
|
||||
fs.writeFileSync(rpmPackageSpecFilePath, rpmPackageSpecsContents)
|
||||
});
|
||||
fs.writeFileSync(rpmPackageSpecFilePath, rpmPackageSpecsContents);
|
||||
|
||||
console.log(`Writing desktop entry file into "${rpmPackageBuildDirPath}"`)
|
||||
const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in'))
|
||||
console.log(`Writing desktop entry file into "${rpmPackageBuildDirPath}"`);
|
||||
const desktopEntryTemplate = fs.readFileSync(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'atom.desktop.in'
|
||||
)
|
||||
);
|
||||
const desktopEntryContents = template(desktopEntryTemplate)({
|
||||
appName: appName,
|
||||
appFileName: atomExecutableName,
|
||||
description: appDescription,
|
||||
installDir: '/usr',
|
||||
iconPath: atomExecutableName
|
||||
})
|
||||
fs.writeFileSync(path.join(rpmPackageBuildDirPath, `${atomExecutableName}.desktop`), desktopEntryContents)
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(rpmPackageBuildDirPath, `${atomExecutableName}.desktop`),
|
||||
desktopEntryContents
|
||||
);
|
||||
|
||||
console.log(`Copying atom.sh into "${rpmPackageBuildDirPath}"`)
|
||||
console.log(`Copying atom.sh into "${rpmPackageBuildDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'atom.sh'),
|
||||
path.join(rpmPackageBuildDirPath, 'atom.sh')
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Copying atom.policy into "${rpmPackageBuildDirPath}"`)
|
||||
console.log(`Copying atom.policy into "${rpmPackageBuildDirPath}"`);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.policy'),
|
||||
path.join(rpmPackageBuildDirPath, 'atom.policy')
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`Generating .rpm package from "${rpmPackageDirPath}"`)
|
||||
spawnSync('rpmbuild', ['-ba', '--clean', rpmPackageSpecFilePath])
|
||||
console.log(`Generating .rpm package from "${rpmPackageDirPath}"`);
|
||||
spawnSync('rpmbuild', ['-ba', '--clean', rpmPackageSpecFilePath]);
|
||||
for (let generatedArch of fs.readdirSync(rpmPackageRpmsDirPath)) {
|
||||
const generatedArchDirPath = path.join(rpmPackageRpmsDirPath, generatedArch)
|
||||
const generatedPackageFileNames = fs.readdirSync(generatedArchDirPath)
|
||||
assert(generatedPackageFileNames.length === 1, 'Generated more than one rpm package')
|
||||
const generatedPackageFilePath = path.join(generatedArchDirPath, generatedPackageFileNames[0])
|
||||
const outputRpmPackageFilePath = path.join(CONFIG.buildOutputPath, `atom.${generatedArch}.rpm`)
|
||||
console.log(`Copying "${generatedPackageFilePath}" into "${outputRpmPackageFilePath}"`)
|
||||
fs.copySync(generatedPackageFilePath, outputRpmPackageFilePath)
|
||||
const generatedArchDirPath = path.join(
|
||||
rpmPackageRpmsDirPath,
|
||||
generatedArch
|
||||
);
|
||||
const generatedPackageFileNames = fs.readdirSync(generatedArchDirPath);
|
||||
assert(
|
||||
generatedPackageFileNames.length === 1,
|
||||
'Generated more than one rpm package'
|
||||
);
|
||||
const generatedPackageFilePath = path.join(
|
||||
generatedArchDirPath,
|
||||
generatedPackageFileNames[0]
|
||||
);
|
||||
const outputRpmPackageFilePath = path.join(
|
||||
CONFIG.buildOutputPath,
|
||||
`atom.${generatedArch}.rpm`
|
||||
);
|
||||
console.log(
|
||||
`Copying "${generatedPackageFilePath}" into "${outputRpmPackageFilePath}"`
|
||||
);
|
||||
fs.copySync(generatedPackageFilePath, outputRpmPackageFilePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,54 +1,73 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const electronInstaller = require('electron-winstaller')
|
||||
const fs = require('fs')
|
||||
const glob = require('glob')
|
||||
const path = require('path')
|
||||
const electronInstaller = require('electron-winstaller');
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = (packagedAppPath) => {
|
||||
const archSuffix = process.arch === 'ia32' ? '' : '-' + process.arch
|
||||
const updateUrlPrefix = process.env.ATOM_UPDATE_URL_PREFIX || 'https://atom.io'
|
||||
module.exports = packagedAppPath => {
|
||||
const archSuffix = process.arch === 'ia32' ? '' : '-' + process.arch;
|
||||
const updateUrlPrefix =
|
||||
process.env.ATOM_UPDATE_URL_PREFIX || 'https://atom.io';
|
||||
const options = {
|
||||
appDirectory: packagedAppPath,
|
||||
authors: 'GitHub Inc.',
|
||||
iconUrl: `https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/${CONFIG.channel}/atom.ico`,
|
||||
loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'),
|
||||
iconUrl: `https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/${
|
||||
CONFIG.channel
|
||||
}/atom.ico`,
|
||||
loadingGif: path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'win',
|
||||
'loading.gif'
|
||||
),
|
||||
outputDirectory: CONFIG.buildOutputPath,
|
||||
noMsi: true,
|
||||
noDelta: CONFIG.channel === 'nightly', // Delta packages are broken for nightly versions past nightly9 due to Squirrel/NuGet limitations
|
||||
remoteReleases: `${updateUrlPrefix}/api/updates${archSuffix}?version=${CONFIG.computedAppVersion}`,
|
||||
remoteReleases: `${updateUrlPrefix}/api/updates${archSuffix}?version=${
|
||||
CONFIG.computedAppVersion
|
||||
}`,
|
||||
setupExe: `AtomSetup${process.arch === 'x64' ? '-x64' : ''}.exe`,
|
||||
setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
|
||||
}
|
||||
setupIcon: path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'app-icons',
|
||||
CONFIG.channel,
|
||||
'atom.ico'
|
||||
)
|
||||
};
|
||||
|
||||
const cleanUp = () => {
|
||||
const releasesPath = `${CONFIG.buildOutputPath}/RELEASES`
|
||||
const releasesPath = `${CONFIG.buildOutputPath}/RELEASES`;
|
||||
if (process.arch === 'x64' && fs.existsSync(releasesPath)) {
|
||||
fs.renameSync(releasesPath, `${releasesPath}-x64`)
|
||||
fs.renameSync(releasesPath, `${releasesPath}-x64`);
|
||||
}
|
||||
|
||||
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/atom-*.nupkg`)) {
|
||||
if (!nupkgPath.includes(CONFIG.computedAppVersion)) {
|
||||
console.log(`Deleting downloaded nupkg for previous version at ${nupkgPath} to prevent it from being stored as an artifact`)
|
||||
fs.unlinkSync(nupkgPath)
|
||||
console.log(
|
||||
`Deleting downloaded nupkg for previous version at ${nupkgPath} to prevent it from being stored as an artifact`
|
||||
);
|
||||
fs.unlinkSync(nupkgPath);
|
||||
} else {
|
||||
if (process.arch === 'x64') {
|
||||
// Use the original .nupkg filename to generate the `atom-x64` name by inserting `-x64` after `atom`
|
||||
const newNupkgPath = nupkgPath.replace('atom-', 'atom-x64-')
|
||||
fs.renameSync(nupkgPath, newNupkgPath)
|
||||
const newNupkgPath = nupkgPath.replace('atom-', 'atom-x64-');
|
||||
fs.renameSync(nupkgPath, newNupkgPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `${CONFIG.buildOutputPath}/${options.setupExe}`
|
||||
}
|
||||
return `${CONFIG.buildOutputPath}/${options.setupExe}`;
|
||||
};
|
||||
|
||||
console.log(`Creating Windows Installer for ${packagedAppPath}`)
|
||||
return electronInstaller.createWindowsInstaller(options)
|
||||
console.log(`Creating Windows Installer for ${packagedAppPath}`);
|
||||
return electronInstaller
|
||||
.createWindowsInstaller(options)
|
||||
.then(cleanUp, error => {
|
||||
cleanUp()
|
||||
return Promise.reject(error)
|
||||
})
|
||||
}
|
||||
cleanUp();
|
||||
return Promise.reject(error);
|
||||
});
|
||||
};
|
||||
|
@ -1,19 +1,22 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function () {
|
||||
process.env['PATH'] =
|
||||
process.env['PATH']
|
||||
.split(';')
|
||||
.filter(function (p) {
|
||||
if (fs.existsSync(path.join(p, 'msbuild.exe'))) {
|
||||
console.log('Excluding "' + p + '" from PATH to avoid msbuild.exe mismatch that causes errors during module installation')
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
.join(';')
|
||||
}
|
||||
module.exports = function() {
|
||||
process.env['PATH'] = process.env['PATH']
|
||||
.split(';')
|
||||
.filter(function(p) {
|
||||
if (fs.existsSync(path.join(p, 'msbuild.exe'))) {
|
||||
console.log(
|
||||
'Excluding "' +
|
||||
p +
|
||||
'" from PATH to avoid msbuild.exe mismatch that causes errors during module installation'
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.join(';');
|
||||
};
|
||||
|
@ -1,28 +1,49 @@
|
||||
const crypto = require('crypto')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const FINGERPRINT_PATH = path.join(CONFIG.repositoryRootPath, 'node_modules', '.dependencies-fingerprint')
|
||||
const CONFIG = require('../config');
|
||||
const FINGERPRINT_PATH = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'node_modules',
|
||||
'.dependencies-fingerprint'
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
write: function () {
|
||||
const fingerprint = this.compute()
|
||||
fs.writeFileSync(FINGERPRINT_PATH, fingerprint)
|
||||
console.log('Wrote Dependencies Fingerprint:', FINGERPRINT_PATH, fingerprint)
|
||||
write: function() {
|
||||
const fingerprint = this.compute();
|
||||
fs.writeFileSync(FINGERPRINT_PATH, fingerprint);
|
||||
console.log(
|
||||
'Wrote Dependencies Fingerprint:',
|
||||
FINGERPRINT_PATH,
|
||||
fingerprint
|
||||
);
|
||||
},
|
||||
read: function () {
|
||||
return fs.existsSync(FINGERPRINT_PATH) ? fs.readFileSync(FINGERPRINT_PATH, 'utf8') : null
|
||||
read: function() {
|
||||
return fs.existsSync(FINGERPRINT_PATH)
|
||||
? fs.readFileSync(FINGERPRINT_PATH, 'utf8')
|
||||
: null;
|
||||
},
|
||||
isOutdated: function () {
|
||||
const fingerprint = this.read()
|
||||
return fingerprint ? fingerprint !== this.compute() : false
|
||||
isOutdated: function() {
|
||||
const fingerprint = this.read();
|
||||
return fingerprint ? fingerprint !== this.compute() : false;
|
||||
},
|
||||
compute: function () {
|
||||
compute: function() {
|
||||
// Include the electron minor version in the fingerprint since that changing requires a re-install
|
||||
const electronVersion = CONFIG.appMetadata.electronVersion.replace(/\.\d+$/, '')
|
||||
const apmVersion = CONFIG.apmMetadata.dependencies['atom-package-manager']
|
||||
const body = electronVersion + apmVersion + process.platform + process.version + process.arch
|
||||
return crypto.createHash('sha1').update(body).digest('hex')
|
||||
const electronVersion = CONFIG.appMetadata.electronVersion.replace(
|
||||
/\.\d+$/,
|
||||
''
|
||||
);
|
||||
const apmVersion = CONFIG.apmMetadata.dependencies['atom-package-manager'];
|
||||
const body =
|
||||
electronVersion +
|
||||
apmVersion +
|
||||
process.platform +
|
||||
process.version +
|
||||
process.arch;
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update(body)
|
||||
.digest('hex');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,19 +1,24 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const syncRequest = require('sync-request')
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const syncRequest = require('sync-request');
|
||||
|
||||
module.exports = function (downloadURL, destinationPath) {
|
||||
console.log(`Downloading file from GitHub Repository to ${destinationPath}`)
|
||||
module.exports = function(downloadURL, destinationPath) {
|
||||
console.log(`Downloading file from GitHub Repository to ${destinationPath}`);
|
||||
const response = syncRequest('GET', downloadURL, {
|
||||
'headers': {'Accept': 'application/vnd.github.v3.raw', 'User-Agent': 'Atom Build'}
|
||||
})
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3.raw',
|
||||
'User-Agent': 'Atom Build'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
fs.mkdirpSync(path.dirname(destinationPath))
|
||||
fs.writeFileSync(destinationPath, response.body)
|
||||
fs.mkdirpSync(path.dirname(destinationPath));
|
||||
fs.writeFileSync(destinationPath, response.body);
|
||||
} else {
|
||||
throw new Error('Error downloading file. HTTP Status ' + response.statusCode + '.')
|
||||
throw new Error(
|
||||
'Error downloading file. HTTP Status ' + response.statusCode + '.'
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,44 +1,55 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const glob = require('glob')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
module.exports = function () {
|
||||
const CONFIG = require('../config');
|
||||
module.exports = function() {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping symbol dumping because minidump is not supported on Windows'.gray)
|
||||
return Promise.resolve()
|
||||
console.log(
|
||||
'Skipping symbol dumping because minidump is not supported on Windows'
|
||||
.gray
|
||||
);
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
console.log(`Dumping symbols in ${CONFIG.symbolsPath}`)
|
||||
const binaryPaths = glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', '**', '*.node'))
|
||||
return Promise.all(binaryPaths.map(dumpSymbol))
|
||||
console.log(`Dumping symbols in ${CONFIG.symbolsPath}`);
|
||||
const binaryPaths = glob.sync(
|
||||
path.join(CONFIG.intermediateAppPath, 'node_modules', '**', '*.node')
|
||||
);
|
||||
return Promise.all(binaryPaths.map(dumpSymbol));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function dumpSymbol (binaryPath) {
|
||||
const minidump = require('minidump')
|
||||
function dumpSymbol(binaryPath) {
|
||||
const minidump = require('minidump');
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
minidump.dumpSymbol(binaryPath, function (error, content) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
minidump.dumpSymbol(binaryPath, function(error, content) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
throw new Error(error)
|
||||
console.error(error);
|
||||
throw new Error(error);
|
||||
} else {
|
||||
const moduleLine = /MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n/.exec(content)
|
||||
const moduleLine = /MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n/.exec(
|
||||
content
|
||||
);
|
||||
if (moduleLine.length !== 3) {
|
||||
const errorMessage = `Invalid output when dumping symbol for ${binaryPath}`
|
||||
console.error(errorMessage)
|
||||
throw new Error(errorMessage)
|
||||
const errorMessage = `Invalid output when dumping symbol for ${binaryPath}`;
|
||||
console.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
} else {
|
||||
const filename = moduleLine[2]
|
||||
const symbolDirPath = path.join(CONFIG.symbolsPath, filename, moduleLine[1])
|
||||
const symbolFilePath = path.join(symbolDirPath, `${filename}.sym`)
|
||||
fs.mkdirpSync(symbolDirPath)
|
||||
fs.writeFileSync(symbolFilePath, content)
|
||||
resolve()
|
||||
const filename = moduleLine[2];
|
||||
const symbolDirPath = path.join(
|
||||
CONFIG.symbolsPath,
|
||||
filename,
|
||||
moduleLine[1]
|
||||
);
|
||||
const symbolFilePath = path.join(symbolDirPath, `${filename}.sym`);
|
||||
fs.mkdirpSync(symbolDirPath);
|
||||
fs.writeFileSync(symbolFilePath, content);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,19 +1,21 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const glob = require('glob')
|
||||
const glob = require('glob');
|
||||
|
||||
module.exports = function (globPaths) {
|
||||
return Promise.all(globPaths.map(g => expandGlobPath(g))).then(paths => paths.reduce((a, b) => a.concat(b), []))
|
||||
}
|
||||
module.exports = function(globPaths) {
|
||||
return Promise.all(globPaths.map(g => expandGlobPath(g))).then(paths =>
|
||||
paths.reduce((a, b) => a.concat(b), [])
|
||||
);
|
||||
};
|
||||
|
||||
function expandGlobPath (globPath) {
|
||||
function expandGlobPath(globPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
glob(globPath, (error, paths) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(paths)
|
||||
resolve(paths);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,53 +1,55 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const donna = require('donna')
|
||||
const tello = require('tello')
|
||||
const joanna = require('joanna')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const donna = require('donna');
|
||||
const tello = require('tello');
|
||||
const joanna = require('joanna');
|
||||
const glob = require('glob');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
const generatedJSONPath = path.join(CONFIG.docsOutputPath, 'atom-api.json')
|
||||
console.log(`Generating API docs at ${generatedJSONPath}`)
|
||||
module.exports = function() {
|
||||
const generatedJSONPath = path.join(CONFIG.docsOutputPath, 'atom-api.json');
|
||||
console.log(`Generating API docs at ${generatedJSONPath}`);
|
||||
|
||||
// Unfortunately, correct relative paths depend on a specific working
|
||||
// directory, but this script should be able to run from anywhere, so we
|
||||
// muck with the cwd temporarily.
|
||||
const oldWorkingDirectoryPath = process.cwd()
|
||||
process.chdir(CONFIG.repositoryRootPath)
|
||||
const coffeeMetadata = donna.generateMetadata(['.'])[0]
|
||||
const jsMetadata = joanna(glob.sync(`src/**/*.js`))
|
||||
process.chdir(oldWorkingDirectoryPath)
|
||||
const oldWorkingDirectoryPath = process.cwd();
|
||||
process.chdir(CONFIG.repositoryRootPath);
|
||||
const coffeeMetadata = donna.generateMetadata(['.'])[0];
|
||||
const jsMetadata = joanna(glob.sync(`src/**/*.js`));
|
||||
process.chdir(oldWorkingDirectoryPath);
|
||||
|
||||
const metadata = {
|
||||
repository: coffeeMetadata.repository,
|
||||
version: coffeeMetadata.version,
|
||||
files: Object.assign(coffeeMetadata.files, jsMetadata.files)
|
||||
};
|
||||
|
||||
const api = tello.digest([metadata]);
|
||||
Object.assign(api.classes, getAPIDocsForDependencies());
|
||||
api.classes = sortObjectByKey(api.classes);
|
||||
|
||||
fs.mkdirpSync(CONFIG.docsOutputPath);
|
||||
fs.writeFileSync(generatedJSONPath, JSON.stringify(api, null, 2));
|
||||
};
|
||||
|
||||
function getAPIDocsForDependencies() {
|
||||
const classes = {};
|
||||
for (let apiJSONPath of glob.sync(
|
||||
`${CONFIG.repositoryRootPath}/node_modules/*/api.json`
|
||||
)) {
|
||||
Object.assign(classes, require(apiJSONPath).classes);
|
||||
}
|
||||
|
||||
const api = tello.digest([metadata])
|
||||
Object.assign(api.classes, getAPIDocsForDependencies())
|
||||
api.classes = sortObjectByKey(api.classes)
|
||||
|
||||
fs.mkdirpSync(CONFIG.docsOutputPath)
|
||||
fs.writeFileSync(generatedJSONPath, JSON.stringify(api, null, 2))
|
||||
return classes;
|
||||
}
|
||||
|
||||
function getAPIDocsForDependencies () {
|
||||
const classes = {}
|
||||
for (let apiJSONPath of glob.sync(`${CONFIG.repositoryRootPath}/node_modules/*/api.json`)) {
|
||||
Object.assign(classes, require(apiJSONPath).classes)
|
||||
}
|
||||
return classes
|
||||
}
|
||||
|
||||
function sortObjectByKey (object) {
|
||||
const sortedObject = {}
|
||||
function sortObjectByKey(object) {
|
||||
const sortedObject = {};
|
||||
for (let keyName of Object.keys(object).sort()) {
|
||||
sortedObject[keyName] = object[keyName]
|
||||
sortedObject[keyName] = object[keyName];
|
||||
}
|
||||
return sortedObject
|
||||
return sortedObject;
|
||||
}
|
||||
|
@ -1,165 +1,263 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const CSON = require('season')
|
||||
const deprecatedPackagesMetadata = require('../deprecated-packages')
|
||||
const fs = require('fs-plus')
|
||||
const normalizePackageData = require('normalize-package-data')
|
||||
const path = require('path')
|
||||
const semver = require('semver')
|
||||
const CSON = require('season');
|
||||
const deprecatedPackagesMetadata = require('../deprecated-packages');
|
||||
const fs = require('fs-plus');
|
||||
const normalizePackageData = require('normalize-package-data');
|
||||
const path = require('path');
|
||||
const semver = require('semver');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
console.log(`Generating metadata for ${path.join(CONFIG.intermediateAppPath, 'package.json')}`)
|
||||
CONFIG.appMetadata._atomPackages = buildBundledPackagesMetadata()
|
||||
CONFIG.appMetadata._atomMenu = buildPlatformMenuMetadata()
|
||||
CONFIG.appMetadata._atomKeymaps = buildPlatformKeymapsMetadata()
|
||||
CONFIG.appMetadata._deprecatedPackages = deprecatedPackagesMetadata
|
||||
CONFIG.appMetadata.version = CONFIG.computedAppVersion
|
||||
checkDeprecatedPackagesMetadata()
|
||||
fs.writeFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'), JSON.stringify(CONFIG.appMetadata))
|
||||
}
|
||||
module.exports = function() {
|
||||
console.log(
|
||||
`Generating metadata for ${path.join(
|
||||
CONFIG.intermediateAppPath,
|
||||
'package.json'
|
||||
)}`
|
||||
);
|
||||
CONFIG.appMetadata._atomPackages = buildBundledPackagesMetadata();
|
||||
CONFIG.appMetadata._atomMenu = buildPlatformMenuMetadata();
|
||||
CONFIG.appMetadata._atomKeymaps = buildPlatformKeymapsMetadata();
|
||||
CONFIG.appMetadata._deprecatedPackages = deprecatedPackagesMetadata;
|
||||
CONFIG.appMetadata.version = CONFIG.computedAppVersion;
|
||||
checkDeprecatedPackagesMetadata();
|
||||
fs.writeFileSync(
|
||||
path.join(CONFIG.intermediateAppPath, 'package.json'),
|
||||
JSON.stringify(CONFIG.appMetadata)
|
||||
);
|
||||
};
|
||||
|
||||
function buildBundledPackagesMetadata () {
|
||||
const packages = {}
|
||||
function buildBundledPackagesMetadata() {
|
||||
const packages = {};
|
||||
for (let packageName of Object.keys(CONFIG.appMetadata.packageDependencies)) {
|
||||
const packagePath = path.join(CONFIG.intermediateAppPath, 'node_modules', packageName)
|
||||
const packageMetadataPath = path.join(packagePath, 'package.json')
|
||||
const packageMetadata = JSON.parse(fs.readFileSync(packageMetadataPath, 'utf8'))
|
||||
normalizePackageData(packageMetadata, (msg) => {
|
||||
if (!msg.match(/No README data$/)) {
|
||||
console.warn(`Invalid package metadata. ${packageMetadata.name}: ${msg}`)
|
||||
}
|
||||
}, true)
|
||||
if (packageMetadata.repository && packageMetadata.repository.url && packageMetadata.repository.type === 'git') {
|
||||
packageMetadata.repository.url = packageMetadata.repository.url.replace(/^git\+/, '')
|
||||
const packagePath = path.join(
|
||||
CONFIG.intermediateAppPath,
|
||||
'node_modules',
|
||||
packageName
|
||||
);
|
||||
const packageMetadataPath = path.join(packagePath, 'package.json');
|
||||
const packageMetadata = JSON.parse(
|
||||
fs.readFileSync(packageMetadataPath, 'utf8')
|
||||
);
|
||||
normalizePackageData(
|
||||
packageMetadata,
|
||||
msg => {
|
||||
if (!msg.match(/No README data$/)) {
|
||||
console.warn(
|
||||
`Invalid package metadata. ${packageMetadata.name}: ${msg}`
|
||||
);
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
if (
|
||||
packageMetadata.repository &&
|
||||
packageMetadata.repository.url &&
|
||||
packageMetadata.repository.type === 'git'
|
||||
) {
|
||||
packageMetadata.repository.url = packageMetadata.repository.url.replace(
|
||||
/^git\+/,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
delete packageMetadata['_from']
|
||||
delete packageMetadata['_id']
|
||||
delete packageMetadata['dist']
|
||||
delete packageMetadata['readme']
|
||||
delete packageMetadata['readmeFilename']
|
||||
delete packageMetadata['_from'];
|
||||
delete packageMetadata['_id'];
|
||||
delete packageMetadata['dist'];
|
||||
delete packageMetadata['readme'];
|
||||
delete packageMetadata['readmeFilename'];
|
||||
|
||||
const packageModuleCache = packageMetadata._atomModuleCache || {}
|
||||
if (packageModuleCache.extensions && packageModuleCache.extensions['.json']) {
|
||||
const index = packageModuleCache.extensions['.json'].indexOf('package.json')
|
||||
const packageModuleCache = packageMetadata._atomModuleCache || {};
|
||||
if (
|
||||
packageModuleCache.extensions &&
|
||||
packageModuleCache.extensions['.json']
|
||||
) {
|
||||
const index = packageModuleCache.extensions['.json'].indexOf(
|
||||
'package.json'
|
||||
);
|
||||
if (index !== -1) {
|
||||
packageModuleCache.extensions['.json'].splice(index, 1)
|
||||
packageModuleCache.extensions['.json'].splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const packageNewMetadata = {metadata: packageMetadata, keymaps: {}, menus: {}, grammarPaths: [], settings: {}}
|
||||
const packageNewMetadata = {
|
||||
metadata: packageMetadata,
|
||||
keymaps: {},
|
||||
menus: {},
|
||||
grammarPaths: [],
|
||||
settings: {}
|
||||
};
|
||||
|
||||
packageNewMetadata.rootDirPath = path.relative(CONFIG.intermediateAppPath, packagePath)
|
||||
packageNewMetadata.rootDirPath = path.relative(
|
||||
CONFIG.intermediateAppPath,
|
||||
packagePath
|
||||
);
|
||||
|
||||
if (packageMetadata.main) {
|
||||
const mainPath = require.resolve(path.resolve(packagePath, packageMetadata.main))
|
||||
packageNewMetadata.main = path.relative(path.join(CONFIG.intermediateAppPath, 'static'), mainPath)
|
||||
const mainPath = require.resolve(
|
||||
path.resolve(packagePath, packageMetadata.main)
|
||||
);
|
||||
packageNewMetadata.main = path.relative(
|
||||
path.join(CONFIG.intermediateAppPath, 'static'),
|
||||
mainPath
|
||||
);
|
||||
// Convert backward slashes to forward slashes in order to allow package
|
||||
// main modules to be required from the snapshot. This is because we use
|
||||
// forward slashes to cache the sources in the snapshot, so we need to use
|
||||
// them here as well.
|
||||
packageNewMetadata.main = packageNewMetadata.main.replace(/\\/g, '/')
|
||||
packageNewMetadata.main = packageNewMetadata.main.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const packageKeymapsPath = path.join(packagePath, 'keymaps')
|
||||
const packageKeymapsPath = path.join(packagePath, 'keymaps');
|
||||
if (fs.existsSync(packageKeymapsPath)) {
|
||||
for (let packageKeymapName of fs.readdirSync(packageKeymapsPath)) {
|
||||
const packageKeymapPath = path.join(packageKeymapsPath, packageKeymapName)
|
||||
if (packageKeymapPath.endsWith('.cson') || packageKeymapPath.endsWith('.json')) {
|
||||
const relativePath = path.relative(CONFIG.intermediateAppPath, packageKeymapPath)
|
||||
packageNewMetadata.keymaps[relativePath] = CSON.readFileSync(packageKeymapPath)
|
||||
const packageKeymapPath = path.join(
|
||||
packageKeymapsPath,
|
||||
packageKeymapName
|
||||
);
|
||||
if (
|
||||
packageKeymapPath.endsWith('.cson') ||
|
||||
packageKeymapPath.endsWith('.json')
|
||||
) {
|
||||
const relativePath = path.relative(
|
||||
CONFIG.intermediateAppPath,
|
||||
packageKeymapPath
|
||||
);
|
||||
packageNewMetadata.keymaps[relativePath] = CSON.readFileSync(
|
||||
packageKeymapPath
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const packageMenusPath = path.join(packagePath, 'menus')
|
||||
const packageMenusPath = path.join(packagePath, 'menus');
|
||||
if (fs.existsSync(packageMenusPath)) {
|
||||
for (let packageMenuName of fs.readdirSync(packageMenusPath)) {
|
||||
const packageMenuPath = path.join(packageMenusPath, packageMenuName)
|
||||
if (packageMenuPath.endsWith('.cson') || packageMenuPath.endsWith('.json')) {
|
||||
const relativePath = path.relative(CONFIG.intermediateAppPath, packageMenuPath)
|
||||
packageNewMetadata.menus[relativePath] = CSON.readFileSync(packageMenuPath)
|
||||
const packageMenuPath = path.join(packageMenusPath, packageMenuName);
|
||||
if (
|
||||
packageMenuPath.endsWith('.cson') ||
|
||||
packageMenuPath.endsWith('.json')
|
||||
) {
|
||||
const relativePath = path.relative(
|
||||
CONFIG.intermediateAppPath,
|
||||
packageMenuPath
|
||||
);
|
||||
packageNewMetadata.menus[relativePath] = CSON.readFileSync(
|
||||
packageMenuPath
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const packageGrammarsPath = path.join(packagePath, 'grammars')
|
||||
for (let packageGrammarPath of fs.listSync(packageGrammarsPath, ['json', 'cson'])) {
|
||||
const relativePath = path.relative(CONFIG.intermediateAppPath, packageGrammarPath)
|
||||
packageNewMetadata.grammarPaths.push(relativePath)
|
||||
const packageGrammarsPath = path.join(packagePath, 'grammars');
|
||||
for (let packageGrammarPath of fs.listSync(packageGrammarsPath, [
|
||||
'json',
|
||||
'cson'
|
||||
])) {
|
||||
const relativePath = path.relative(
|
||||
CONFIG.intermediateAppPath,
|
||||
packageGrammarPath
|
||||
);
|
||||
packageNewMetadata.grammarPaths.push(relativePath);
|
||||
}
|
||||
|
||||
const packageSettingsPath = path.join(packagePath, 'settings')
|
||||
for (let packageSettingPath of fs.listSync(packageSettingsPath, ['json', 'cson'])) {
|
||||
const relativePath = path.relative(CONFIG.intermediateAppPath, packageSettingPath)
|
||||
packageNewMetadata.settings[relativePath] = CSON.readFileSync(packageSettingPath)
|
||||
const packageSettingsPath = path.join(packagePath, 'settings');
|
||||
for (let packageSettingPath of fs.listSync(packageSettingsPath, [
|
||||
'json',
|
||||
'cson'
|
||||
])) {
|
||||
const relativePath = path.relative(
|
||||
CONFIG.intermediateAppPath,
|
||||
packageSettingPath
|
||||
);
|
||||
packageNewMetadata.settings[relativePath] = CSON.readFileSync(
|
||||
packageSettingPath
|
||||
);
|
||||
}
|
||||
|
||||
const packageStyleSheetsPath = path.join(packagePath, 'styles')
|
||||
let styleSheets = null
|
||||
const packageStyleSheetsPath = path.join(packagePath, 'styles');
|
||||
let styleSheets = null;
|
||||
if (packageMetadata.mainStyleSheet) {
|
||||
styleSheets = [fs.resolve(packagePath, packageMetadata.mainStyleSheet)]
|
||||
styleSheets = [fs.resolve(packagePath, packageMetadata.mainStyleSheet)];
|
||||
} else if (packageMetadata.styleSheets) {
|
||||
styleSheets = packageMetadata.styleSheets.map((name) => (
|
||||
styleSheets = packageMetadata.styleSheets.map(name =>
|
||||
fs.resolve(packageStyleSheetsPath, name, ['css', 'less', ''])
|
||||
))
|
||||
);
|
||||
} else {
|
||||
const indexStylesheet = fs.resolve(packagePath, 'index', ['css', 'less'])
|
||||
const indexStylesheet = fs.resolve(packagePath, 'index', ['css', 'less']);
|
||||
if (indexStylesheet) {
|
||||
styleSheets = [indexStylesheet]
|
||||
styleSheets = [indexStylesheet];
|
||||
} else {
|
||||
styleSheets = fs.listSync(packageStyleSheetsPath, ['css', 'less'])
|
||||
styleSheets = fs.listSync(packageStyleSheetsPath, ['css', 'less']);
|
||||
}
|
||||
}
|
||||
|
||||
packageNewMetadata.styleSheetPaths =
|
||||
styleSheets.map(styleSheetPath => path.relative(packagePath, styleSheetPath))
|
||||
packageNewMetadata.styleSheetPaths = styleSheets.map(styleSheetPath =>
|
||||
path.relative(packagePath, styleSheetPath)
|
||||
);
|
||||
|
||||
packages[packageMetadata.name] = packageNewMetadata
|
||||
packages[packageMetadata.name] = packageNewMetadata;
|
||||
if (packageModuleCache.extensions) {
|
||||
for (let extension of Object.keys(packageModuleCache.extensions)) {
|
||||
const paths = packageModuleCache.extensions[extension]
|
||||
const paths = packageModuleCache.extensions[extension];
|
||||
if (paths.length === 0) {
|
||||
delete packageModuleCache.extensions[extension]
|
||||
delete packageModuleCache.extensions[extension];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return packages
|
||||
return packages;
|
||||
}
|
||||
|
||||
function buildPlatformMenuMetadata () {
|
||||
const menuPath = path.join(CONFIG.repositoryRootPath, 'menus', `${process.platform}.cson`)
|
||||
function buildPlatformMenuMetadata() {
|
||||
const menuPath = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'menus',
|
||||
`${process.platform}.cson`
|
||||
);
|
||||
if (fs.existsSync(menuPath)) {
|
||||
return CSON.readFileSync(menuPath)
|
||||
return CSON.readFileSync(menuPath);
|
||||
} else {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function buildPlatformKeymapsMetadata () {
|
||||
const invalidPlatforms = ['darwin', 'freebsd', 'linux', 'sunos', 'win32'].filter(p => p !== process.platform)
|
||||
const keymapsPath = path.join(CONFIG.repositoryRootPath, 'keymaps')
|
||||
const keymaps = {}
|
||||
function buildPlatformKeymapsMetadata() {
|
||||
const invalidPlatforms = [
|
||||
'darwin',
|
||||
'freebsd',
|
||||
'linux',
|
||||
'sunos',
|
||||
'win32'
|
||||
].filter(p => p !== process.platform);
|
||||
const keymapsPath = path.join(CONFIG.repositoryRootPath, 'keymaps');
|
||||
const keymaps = {};
|
||||
for (let keymapName of fs.readdirSync(keymapsPath)) {
|
||||
const keymapPath = path.join(keymapsPath, keymapName)
|
||||
const keymapPath = path.join(keymapsPath, keymapName);
|
||||
if (keymapPath.endsWith('.cson') || keymapPath.endsWith('.json')) {
|
||||
const keymapPlatform = path.basename(keymapPath, path.extname(keymapPath))
|
||||
const keymapPlatform = path.basename(
|
||||
keymapPath,
|
||||
path.extname(keymapPath)
|
||||
);
|
||||
if (invalidPlatforms.indexOf(keymapPlatform) === -1) {
|
||||
keymaps[path.basename(keymapPath)] = CSON.readFileSync(keymapPath)
|
||||
keymaps[path.basename(keymapPath)] = CSON.readFileSync(keymapPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keymaps
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
function checkDeprecatedPackagesMetadata () {
|
||||
function checkDeprecatedPackagesMetadata() {
|
||||
for (let packageName of Object.keys(deprecatedPackagesMetadata)) {
|
||||
const packageMetadata = deprecatedPackagesMetadata[packageName]
|
||||
if (packageMetadata.version && !semver.validRange(packageMetadata.version)) {
|
||||
throw new Error(`Invalid range: ${packageMetadata.version} (${packageName}).`)
|
||||
const packageMetadata = deprecatedPackagesMetadata[packageName];
|
||||
if (
|
||||
packageMetadata.version &&
|
||||
!semver.validRange(packageMetadata.version)
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid range: ${packageMetadata.version} (${packageName}).`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const ModuleCache = require('../../src/module-cache')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ModuleCache = require('../../src/module-cache');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
console.log(`Generating module cache for ${CONFIG.intermediateAppPath}`)
|
||||
module.exports = function() {
|
||||
console.log(`Generating module cache for ${CONFIG.intermediateAppPath}`);
|
||||
for (let packageName of Object.keys(CONFIG.appMetadata.packageDependencies)) {
|
||||
ModuleCache.create(path.join(CONFIG.intermediateAppPath, 'node_modules', packageName))
|
||||
ModuleCache.create(
|
||||
path.join(CONFIG.intermediateAppPath, 'node_modules', packageName)
|
||||
);
|
||||
}
|
||||
ModuleCache.create(CONFIG.intermediateAppPath)
|
||||
const newMetadata = JSON.parse(fs.readFileSync(path.join(CONFIG.intermediateAppPath, 'package.json')))
|
||||
ModuleCache.create(CONFIG.intermediateAppPath);
|
||||
const newMetadata = JSON.parse(
|
||||
fs.readFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'))
|
||||
);
|
||||
for (let folder of newMetadata._atomModuleCache.folders) {
|
||||
if (folder.paths.indexOf('') !== -1) {
|
||||
folder.paths = [
|
||||
@ -23,9 +27,12 @@ module.exports = function () {
|
||||
'src/main-process',
|
||||
'static',
|
||||
'vendor'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
CONFIG.appMetadata = newMetadata
|
||||
fs.writeFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'), JSON.stringify(CONFIG.appMetadata))
|
||||
}
|
||||
CONFIG.appMetadata = newMetadata;
|
||||
fs.writeFileSync(
|
||||
path.join(CONFIG.intermediateAppPath, 'package.json'),
|
||||
JSON.stringify(CONFIG.appMetadata)
|
||||
);
|
||||
};
|
||||
|
@ -1,140 +1,321 @@
|
||||
const childProcess = require('child_process')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const electronLink = require('electron-link')
|
||||
const terser = require('terser')
|
||||
const CONFIG = require('../config')
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const electronLink = require('electron-link');
|
||||
const terser = require('terser');
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (packagedAppPath) {
|
||||
const snapshotScriptPath = path.join(CONFIG.buildOutputPath, 'startup.js')
|
||||
const coreModules = new Set(['electron', 'atom', 'shell', 'WNdb', 'lapack', 'remote'])
|
||||
const baseDirPath = path.join(CONFIG.intermediateAppPath, 'static')
|
||||
let processedFiles = 0
|
||||
module.exports = function(packagedAppPath) {
|
||||
const snapshotScriptPath = path.join(CONFIG.buildOutputPath, 'startup.js');
|
||||
const coreModules = new Set([
|
||||
'electron',
|
||||
'atom',
|
||||
'shell',
|
||||
'WNdb',
|
||||
'lapack',
|
||||
'remote'
|
||||
]);
|
||||
const baseDirPath = path.join(CONFIG.intermediateAppPath, 'static');
|
||||
let processedFiles = 0;
|
||||
|
||||
return electronLink({
|
||||
baseDirPath,
|
||||
mainPath: path.resolve(baseDirPath, '..', 'src', 'initialize-application-window.js'),
|
||||
mainPath: path.resolve(
|
||||
baseDirPath,
|
||||
'..',
|
||||
'src',
|
||||
'initialize-application-window.js'
|
||||
),
|
||||
cachePath: path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'),
|
||||
auxiliaryData: CONFIG.snapshotAuxiliaryData,
|
||||
shouldExcludeModule: ({requiringModulePath, requiredModulePath}) => {
|
||||
shouldExcludeModule: ({ requiringModulePath, requiredModulePath }) => {
|
||||
if (processedFiles > 0) {
|
||||
process.stdout.write('\r')
|
||||
process.stdout.write('\r');
|
||||
}
|
||||
process.stdout.write(`Generating snapshot script at "${snapshotScriptPath}" (${++processedFiles})`)
|
||||
process.stdout.write(
|
||||
`Generating snapshot script at "${snapshotScriptPath}" (${++processedFiles})`
|
||||
);
|
||||
|
||||
const requiringModuleRelativePath = path.relative(baseDirPath, requiringModulePath)
|
||||
const requiredModuleRelativePath = path.relative(baseDirPath, requiredModulePath)
|
||||
const requiringModuleRelativePath = path.relative(
|
||||
baseDirPath,
|
||||
requiringModulePath
|
||||
);
|
||||
const requiredModuleRelativePath = path.relative(
|
||||
baseDirPath,
|
||||
requiredModulePath
|
||||
);
|
||||
return (
|
||||
requiredModulePath.endsWith('.node') ||
|
||||
coreModules.has(requiredModulePath) ||
|
||||
requiringModuleRelativePath.endsWith(path.join('node_modules/xregexp/xregexp-all.js')) ||
|
||||
(requiredModuleRelativePath.startsWith(path.join('..', 'src')) && requiredModuleRelativePath.endsWith('-element.js')) ||
|
||||
requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'dugite')) ||
|
||||
requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'markdown-preview', 'node_modules', 'yaml-front-matter')) ||
|
||||
requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'markdown-preview', 'node_modules', 'cheerio')) ||
|
||||
requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'markdown-preview', 'node_modules', 'marked')) ||
|
||||
requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'typescript-simple')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'fs-extra', 'lib', 'index.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'graceful-fs', 'graceful-fs.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'htmlparser2', 'lib', 'index.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'minimatch', 'minimatch.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'request', 'index.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'request', 'request.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'superstring', 'index.js')) ||
|
||||
requiredModuleRelativePath.endsWith(path.join('node_modules', 'temp', 'lib', 'temp.js')) ||
|
||||
requiringModuleRelativePath.endsWith(
|
||||
path.join('node_modules/xregexp/xregexp-all.js')
|
||||
) ||
|
||||
(requiredModuleRelativePath.startsWith(path.join('..', 'src')) &&
|
||||
requiredModuleRelativePath.endsWith('-element.js')) ||
|
||||
requiredModuleRelativePath.startsWith(
|
||||
path.join('..', 'node_modules', 'dugite')
|
||||
) ||
|
||||
requiredModuleRelativePath.startsWith(
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'markdown-preview',
|
||||
'node_modules',
|
||||
'yaml-front-matter'
|
||||
)
|
||||
) ||
|
||||
requiredModuleRelativePath.startsWith(
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'markdown-preview',
|
||||
'node_modules',
|
||||
'cheerio'
|
||||
)
|
||||
) ||
|
||||
requiredModuleRelativePath.startsWith(
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'markdown-preview',
|
||||
'node_modules',
|
||||
'marked'
|
||||
)
|
||||
) ||
|
||||
requiredModuleRelativePath.startsWith(
|
||||
path.join('..', 'node_modules', 'typescript-simple')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join(
|
||||
'node_modules',
|
||||
'coffee-script',
|
||||
'lib',
|
||||
'coffee-script',
|
||||
'register.js'
|
||||
)
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'fs-extra', 'lib', 'index.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'graceful-fs', 'graceful-fs.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'htmlparser2', 'lib', 'index.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'minimatch', 'minimatch.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'request', 'index.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'request', 'request.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'superstring', 'index.js')
|
||||
) ||
|
||||
requiredModuleRelativePath.endsWith(
|
||||
path.join('node_modules', 'temp', 'lib', 'temp.js')
|
||||
) ||
|
||||
requiredModuleRelativePath === path.join('..', 'exports', 'atom.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'src', 'electron-shims.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'debug', 'node.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'glob', 'glob.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'iconv-lite', 'lib', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'lib', 'less', 'fs.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'lib', 'less-node', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'lodash.isequal', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'node-fetch', 'lib', 'fetch-error.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'oniguruma', 'src', 'oniguruma.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'resolve', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'resolve', 'lib', 'core.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'settings-view', 'node_modules', 'glob', 'glob.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'spellchecker', 'lib', 'spellchecker.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'spelling-manager', 'node_modules', 'natural', 'lib', 'natural', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'tar', 'tar.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'ls-archive', 'node_modules', 'tar', 'tar.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'tree-sitter', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'yauzl', 'index.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', '@atom', 'fuzzy-native', 'lib', 'main.js') ||
|
||||
requiredModuleRelativePath === path.join('..', 'node_modules', 'vscode-ripgrep', 'lib', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'src', 'electron-shims.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'atom-keymap',
|
||||
'lib',
|
||||
'command-event.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'babel-core', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'debug', 'node.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'glob', 'glob.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'iconv-lite', 'lib', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'less', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'less', 'lib', 'less', 'fs.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'less',
|
||||
'lib',
|
||||
'less-node',
|
||||
'index.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'lodash.isequal', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'node-fetch',
|
||||
'lib',
|
||||
'fetch-error.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'oniguruma', 'src', 'oniguruma.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'resolve', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'resolve', 'lib', 'core.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'settings-view',
|
||||
'node_modules',
|
||||
'glob',
|
||||
'glob.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'spellchecker',
|
||||
'lib',
|
||||
'spellchecker.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'spelling-manager',
|
||||
'node_modules',
|
||||
'natural',
|
||||
'lib',
|
||||
'natural',
|
||||
'index.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'tar', 'tar.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'ls-archive',
|
||||
'node_modules',
|
||||
'tar',
|
||||
'tar.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'tree-sitter', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'yauzl', 'index.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'@atom',
|
||||
'fuzzy-native',
|
||||
'lib',
|
||||
'main.js'
|
||||
) ||
|
||||
requiredModuleRelativePath ===
|
||||
path.join(
|
||||
'..',
|
||||
'node_modules',
|
||||
'vscode-ripgrep',
|
||||
'lib',
|
||||
'index.js'
|
||||
) ||
|
||||
// The startup-time script is used by both the renderer and the main process and having it in the
|
||||
// snapshot causes issues.
|
||||
requiredModuleRelativePath === path.join('..', 'src', 'startup-time.js')
|
||||
)
|
||||
);
|
||||
}
|
||||
}).then(({snapshotScript}) => {
|
||||
process.stdout.write('\n')
|
||||
}).then(({ snapshotScript }) => {
|
||||
process.stdout.write('\n');
|
||||
|
||||
process.stdout.write('Minifying startup script')
|
||||
process.stdout.write('Minifying startup script');
|
||||
const minification = terser.minify(snapshotScript, {
|
||||
keep_fnames: true,
|
||||
keep_classnames: true,
|
||||
compress: {keep_fargs: true, keep_infinity: true}
|
||||
})
|
||||
if (minification.error) throw minification.error
|
||||
process.stdout.write('\n')
|
||||
fs.writeFileSync(snapshotScriptPath, minification.code)
|
||||
compress: { keep_fargs: true, keep_infinity: true }
|
||||
});
|
||||
if (minification.error) throw minification.error;
|
||||
process.stdout.write('\n');
|
||||
fs.writeFileSync(snapshotScriptPath, minification.code);
|
||||
|
||||
console.log('Verifying if snapshot can be executed via `mksnapshot`')
|
||||
const verifySnapshotScriptPath = path.join(CONFIG.repositoryRootPath, 'script', 'verify-snapshot-script')
|
||||
let nodeBundledInElectronPath
|
||||
console.log('Verifying if snapshot can be executed via `mksnapshot`');
|
||||
const verifySnapshotScriptPath = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'script',
|
||||
'verify-snapshot-script'
|
||||
);
|
||||
let nodeBundledInElectronPath;
|
||||
if (process.platform === 'darwin') {
|
||||
const executableName = CONFIG.appName
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'Contents', 'MacOS', executableName)
|
||||
const executableName = CONFIG.appName;
|
||||
nodeBundledInElectronPath = path.join(
|
||||
packagedAppPath,
|
||||
'Contents',
|
||||
'MacOS',
|
||||
executableName
|
||||
);
|
||||
} else if (process.platform === 'win32') {
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom.exe')
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom.exe');
|
||||
} else {
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom')
|
||||
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom');
|
||||
}
|
||||
childProcess.execFileSync(
|
||||
nodeBundledInElectronPath,
|
||||
[verifySnapshotScriptPath, snapshotScriptPath],
|
||||
{env: Object.assign({}, process.env, {ELECTRON_RUN_AS_NODE: 1})}
|
||||
)
|
||||
{ env: Object.assign({}, process.env, { ELECTRON_RUN_AS_NODE: 1 }) }
|
||||
);
|
||||
|
||||
console.log('Generating startup blob with mksnapshot')
|
||||
childProcess.spawnSync(
|
||||
process.execPath, [
|
||||
path.join(CONFIG.repositoryRootPath, 'script', 'node_modules', 'electron-mksnapshot', 'mksnapshot.js'),
|
||||
snapshotScriptPath,
|
||||
'--output_dir',
|
||||
CONFIG.buildOutputPath
|
||||
]
|
||||
)
|
||||
console.log('Generating startup blob with mksnapshot');
|
||||
childProcess.spawnSync(process.execPath, [
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'script',
|
||||
'node_modules',
|
||||
'electron-mksnapshot',
|
||||
'mksnapshot.js'
|
||||
),
|
||||
snapshotScriptPath,
|
||||
'--output_dir',
|
||||
CONFIG.buildOutputPath
|
||||
]);
|
||||
|
||||
let startupBlobDestinationPath
|
||||
let startupBlobDestinationPath;
|
||||
if (process.platform === 'darwin') {
|
||||
startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources`
|
||||
startupBlobDestinationPath = `${packagedAppPath}/Contents/Frameworks/Electron Framework.framework/Resources`;
|
||||
} else {
|
||||
startupBlobDestinationPath = packagedAppPath
|
||||
startupBlobDestinationPath = packagedAppPath;
|
||||
}
|
||||
|
||||
const snapshotBinaries = ['v8_context_snapshot.bin', 'snapshot_blob.bin']
|
||||
const snapshotBinaries = ['v8_context_snapshot.bin', 'snapshot_blob.bin'];
|
||||
for (let snapshotBinary of snapshotBinaries) {
|
||||
const destinationPath = path.join(startupBlobDestinationPath, snapshotBinary)
|
||||
console.log(`Moving generated startup blob into "${destinationPath}"`)
|
||||
const destinationPath = path.join(
|
||||
startupBlobDestinationPath,
|
||||
snapshotBinary
|
||||
);
|
||||
console.log(`Moving generated startup blob into "${destinationPath}"`);
|
||||
try {
|
||||
fs.unlinkSync(destinationPath)
|
||||
fs.unlinkSync(destinationPath);
|
||||
} catch (err) {
|
||||
// Doesn't matter if the file doesn't exist already
|
||||
if (!err.code || err.code !== 'ENOENT') {
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
fs.renameSync(path.join(CONFIG.buildOutputPath, snapshotBinary), destinationPath)
|
||||
fs.renameSync(
|
||||
path.join(CONFIG.buildOutputPath, snapshotBinary),
|
||||
destinationPath
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1,38 +1,46 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const legalEagle = require('legal-eagle')
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const legalEagle = require('legal-eagle');
|
||||
|
||||
const licenseOverrides = require('../license-overrides')
|
||||
const CONFIG = require('../config')
|
||||
const licenseOverrides = require('../license-overrides');
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
legalEagle({path: CONFIG.repositoryRootPath, overrides: licenseOverrides}, (err, packagesLicenses) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
throw new Error(err)
|
||||
} else {
|
||||
let text =
|
||||
fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'LICENSE.md'), 'utf8') + '\n\n' +
|
||||
'This application bundles the following third-party packages in accordance\n' +
|
||||
'with the following licenses:\n\n'
|
||||
for (let packageName of Object.keys(packagesLicenses).sort()) {
|
||||
const packageLicense = packagesLicenses[packageName]
|
||||
text += '-------------------------------------------------------------------------\n\n'
|
||||
text += `Package: ${packageName}\n`
|
||||
text += `License: ${packageLicense.license}\n`
|
||||
if (packageLicense.source) {
|
||||
text += `License Source: ${packageLicense.source}\n`
|
||||
legalEagle(
|
||||
{ path: CONFIG.repositoryRootPath, overrides: licenseOverrides },
|
||||
(err, packagesLicenses) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
throw new Error(err);
|
||||
} else {
|
||||
let text =
|
||||
fs.readFileSync(
|
||||
path.join(CONFIG.repositoryRootPath, 'LICENSE.md'),
|
||||
'utf8'
|
||||
) +
|
||||
'\n\n' +
|
||||
'This application bundles the following third-party packages in accordance\n' +
|
||||
'with the following licenses:\n\n';
|
||||
for (let packageName of Object.keys(packagesLicenses).sort()) {
|
||||
const packageLicense = packagesLicenses[packageName];
|
||||
text +=
|
||||
'-------------------------------------------------------------------------\n\n';
|
||||
text += `Package: ${packageName}\n`;
|
||||
text += `License: ${packageLicense.license}\n`;
|
||||
if (packageLicense.source) {
|
||||
text += `License Source: ${packageLicense.source}\n`;
|
||||
}
|
||||
if (packageLicense.sourceText) {
|
||||
text += `Source Text:\n\n${packageLicense.sourceText}`;
|
||||
}
|
||||
text += '\n';
|
||||
}
|
||||
if (packageLicense.sourceText) {
|
||||
text += `Source Text:\n\n${packageLicense.sourceText}`
|
||||
}
|
||||
text += '\n'
|
||||
resolve(text);
|
||||
}
|
||||
resolve(text)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
@ -1,24 +1,29 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const os = require('os')
|
||||
const passwdUser = require('passwd-user')
|
||||
const path = require('path')
|
||||
const os = require('os');
|
||||
const passwdUser = require('passwd-user');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function (aPath) {
|
||||
module.exports = function(aPath) {
|
||||
if (!aPath.startsWith('~')) {
|
||||
return aPath
|
||||
return aPath;
|
||||
}
|
||||
|
||||
const sepIndex = aPath.indexOf(path.sep)
|
||||
const user = (sepIndex < 0) ? aPath.substring(1) : aPath.substring(1, sepIndex)
|
||||
const rest = (sepIndex < 0) ? '' : aPath.substring(sepIndex)
|
||||
const home = (user === '') ? os.homedir() : (() => {
|
||||
const passwd = passwdUser.sync(user)
|
||||
if (passwd === undefined) {
|
||||
throw new Error(`Failed to expand the tilde in ${aPath} - user "${user}" does not exist`)
|
||||
}
|
||||
return passwd.homedir
|
||||
})()
|
||||
const sepIndex = aPath.indexOf(path.sep);
|
||||
const user = sepIndex < 0 ? aPath.substring(1) : aPath.substring(1, sepIndex);
|
||||
const rest = sepIndex < 0 ? '' : aPath.substring(sepIndex);
|
||||
const home =
|
||||
user === ''
|
||||
? os.homedir()
|
||||
: (() => {
|
||||
const passwd = passwdUser.sync(user);
|
||||
if (passwd === undefined) {
|
||||
throw new Error(
|
||||
`Failed to expand the tilde in ${aPath} - user "${user}" does not exist`
|
||||
);
|
||||
}
|
||||
return passwd.homedir;
|
||||
})();
|
||||
|
||||
return `${home}${rest}`
|
||||
}
|
||||
return `${home}${rest}`;
|
||||
};
|
||||
|
@ -1,11 +1,14 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const path = require('path')
|
||||
const CONFIG = require('../config')
|
||||
const path = require('path');
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (filePath) {
|
||||
return !EXCLUDED_PATHS_REGEXP.test(filePath) || INCLUDED_PATHS_REGEXP.test(filePath)
|
||||
}
|
||||
module.exports = function(filePath) {
|
||||
return (
|
||||
!EXCLUDED_PATHS_REGEXP.test(filePath) ||
|
||||
INCLUDED_PATHS_REGEXP.test(filePath)
|
||||
);
|
||||
};
|
||||
|
||||
const EXCLUDE_REGEXPS_SOURCES = [
|
||||
escapeRegExp('.DS_Store'),
|
||||
@ -35,7 +38,9 @@ const EXCLUDE_REGEXPS_SOURCES = [
|
||||
escapeRegExp(path.join('npm', 'node_modules', '.bin', 'starwars')),
|
||||
escapeRegExp(path.join('pegjs', 'examples')),
|
||||
escapeRegExp(path.join('get-parameter-names', 'node_modules', 'testla')),
|
||||
escapeRegExp(path.join('get-parameter-names', 'node_modules', '.bin', 'testla')),
|
||||
escapeRegExp(
|
||||
path.join('get-parameter-names', 'node_modules', '.bin', 'testla')
|
||||
),
|
||||
escapeRegExp(path.join('jasmine-reporters', 'ext')),
|
||||
escapeRegExp(path.join('node_modules', 'nan')),
|
||||
escapeRegExp(path.join('node_modules', 'native-mate')),
|
||||
@ -53,7 +58,9 @@ const EXCLUDE_REGEXPS_SOURCES = [
|
||||
escapeRegExp(path.join('node_modules', 'loophole')),
|
||||
escapeRegExp(path.join('node_modules', 'pegjs')),
|
||||
escapeRegExp(path.join('node_modules', '.bin', 'pegjs')),
|
||||
escapeRegExp(path.join('node_modules', 'spellchecker', 'vendor', 'hunspell') + path.sep) + '.*',
|
||||
escapeRegExp(
|
||||
path.join('node_modules', 'spellchecker', 'vendor', 'hunspell') + path.sep
|
||||
) + '.*',
|
||||
|
||||
// node_modules of the fuzzy-native package are only required for building it.
|
||||
escapeRegExp(path.join('node_modules', 'fuzzy-native', 'node_modules')),
|
||||
@ -66,34 +73,59 @@ const EXCLUDE_REGEXPS_SOURCES = [
|
||||
escapeRegExp(path.sep) + '.+\\.target.mk$',
|
||||
escapeRegExp(path.sep) + 'linker\\.lock$',
|
||||
escapeRegExp(path.join('build', 'Release') + path.sep) + '.+\\.node\\.dSYM',
|
||||
escapeRegExp(path.join('build', 'Release') + path.sep) + '.*\\.(pdb|lib|exp|map|ipdb|iobj)',
|
||||
escapeRegExp(path.join('build', 'Release') + path.sep) +
|
||||
'.*\\.(pdb|lib|exp|map|ipdb|iobj)',
|
||||
|
||||
// Ignore node_module files we won't need at runtime
|
||||
'node_modules' + escapeRegExp(path.sep) + '.*' + escapeRegExp(path.sep) + '_*te?sts?_*' + escapeRegExp(path.sep),
|
||||
'node_modules' + escapeRegExp(path.sep) + '.*' + escapeRegExp(path.sep) + 'examples?' + escapeRegExp(path.sep),
|
||||
'node_modules' +
|
||||
escapeRegExp(path.sep) +
|
||||
'.*' +
|
||||
escapeRegExp(path.sep) +
|
||||
'_*te?sts?_*' +
|
||||
escapeRegExp(path.sep),
|
||||
'node_modules' +
|
||||
escapeRegExp(path.sep) +
|
||||
'.*' +
|
||||
escapeRegExp(path.sep) +
|
||||
'examples?' +
|
||||
escapeRegExp(path.sep),
|
||||
'node_modules' + escapeRegExp(path.sep) + '.*' + '\\.d\\.ts$',
|
||||
'node_modules' + escapeRegExp(path.sep) + '.*' + '\\.js\\.map$',
|
||||
'.*' + escapeRegExp(path.sep) + 'test.*\\.html$'
|
||||
]
|
||||
];
|
||||
|
||||
// Ignore spec directories in all bundled packages
|
||||
for (let packageName in CONFIG.appMetadata.packageDependencies) {
|
||||
EXCLUDE_REGEXPS_SOURCES.push('^' + escapeRegExp(path.join(CONFIG.repositoryRootPath, 'node_modules', packageName, 'spec')))
|
||||
EXCLUDE_REGEXPS_SOURCES.push(
|
||||
'^' +
|
||||
escapeRegExp(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'node_modules',
|
||||
packageName,
|
||||
'spec'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Ignore Hunspell dictionaries only on macOS.
|
||||
if (process.platform === 'darwin') {
|
||||
EXCLUDE_REGEXPS_SOURCES.push(escapeRegExp(path.join('spellchecker', 'vendor', 'hunspell_dictionaries')))
|
||||
EXCLUDE_REGEXPS_SOURCES.push(
|
||||
escapeRegExp(path.join('spellchecker', 'vendor', 'hunspell_dictionaries'))
|
||||
);
|
||||
}
|
||||
|
||||
const EXCLUDED_PATHS_REGEXP = new RegExp(
|
||||
EXCLUDE_REGEXPS_SOURCES.map(path => `(${path})`).join('|')
|
||||
)
|
||||
);
|
||||
|
||||
const INCLUDED_PATHS_REGEXP = new RegExp(
|
||||
escapeRegExp(path.join('node_modules', 'node-gyp', 'src', 'win_delay_load_hook.cc'))
|
||||
)
|
||||
escapeRegExp(
|
||||
path.join('node_modules', 'node-gyp', 'src', 'win_delay_load_hook.cc')
|
||||
)
|
||||
);
|
||||
|
||||
function escapeRegExp (string) {
|
||||
return string.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&')
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&');
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const childProcess = require('child_process')
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (ci) {
|
||||
console.log('Installing apm')
|
||||
module.exports = function(ci) {
|
||||
console.log('Installing apm');
|
||||
// npm ci leaves apm with a bunch of unmet dependencies
|
||||
childProcess.execFileSync(
|
||||
CONFIG.getNpmBinPath(),
|
||||
['--global-style', '--loglevel=error', 'install'],
|
||||
{env: process.env, cwd: CONFIG.apmRootPath}
|
||||
)
|
||||
}
|
||||
{ env: process.env, cwd: CONFIG.apmRootPath }
|
||||
);
|
||||
};
|
||||
|
@ -1,22 +1,26 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs-extra')
|
||||
const handleTilde = require('./handle-tilde')
|
||||
const path = require('path')
|
||||
const template = require('lodash.template')
|
||||
const startCase = require('lodash.startcase')
|
||||
const execSync = require('child_process').execSync
|
||||
const fs = require('fs-extra');
|
||||
const handleTilde = require('./handle-tilde');
|
||||
const path = require('path');
|
||||
const template = require('lodash.template');
|
||||
const startCase = require('lodash.startcase');
|
||||
const execSync = require('child_process').execSync;
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
function install (installationDirPath, packagedAppFileName, packagedAppPath) {
|
||||
function install(installationDirPath, packagedAppFileName, packagedAppPath) {
|
||||
if (fs.existsSync(installationDirPath)) {
|
||||
console.log(`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`)
|
||||
fs.removeSync(installationDirPath)
|
||||
console.log(
|
||||
`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`
|
||||
);
|
||||
fs.removeSync(installationDirPath);
|
||||
}
|
||||
|
||||
console.log(`Installing "${packagedAppFileName}" at "${installationDirPath}"`)
|
||||
fs.copySync(packagedAppPath, installationDirPath)
|
||||
console.log(
|
||||
`Installing "${packagedAppFileName}" at "${installationDirPath}"`
|
||||
);
|
||||
fs.copySync(packagedAppPath, installationDirPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,132 +30,205 @@ function install (installationDirPath, packagedAppFileName, packagedAppPath) {
|
||||
* and the XDG Base Directory Specification:
|
||||
* https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
|
||||
*/
|
||||
function findBaseIconThemeDirPath () {
|
||||
const defaultBaseIconThemeDir = '/usr/share/icons/hicolor'
|
||||
const dataDirsString = process.env.XDG_DATA_DIRS
|
||||
function findBaseIconThemeDirPath() {
|
||||
const defaultBaseIconThemeDir = '/usr/share/icons/hicolor';
|
||||
const dataDirsString = process.env.XDG_DATA_DIRS;
|
||||
if (dataDirsString) {
|
||||
const dataDirs = dataDirsString.split(path.delimiter)
|
||||
const dataDirs = dataDirsString.split(path.delimiter);
|
||||
if (dataDirs.includes('/usr/share/') || dataDirs.includes('/usr/share')) {
|
||||
return defaultBaseIconThemeDir
|
||||
return defaultBaseIconThemeDir;
|
||||
} else {
|
||||
return path.join(dataDirs[0], 'icons', 'hicolor')
|
||||
return path.join(dataDirs[0], 'icons', 'hicolor');
|
||||
}
|
||||
} else {
|
||||
return defaultBaseIconThemeDir
|
||||
return defaultBaseIconThemeDir;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function (packagedAppPath, installDir) {
|
||||
const packagedAppFileName = path.basename(packagedAppPath)
|
||||
module.exports = function(packagedAppPath, installDir) {
|
||||
const packagedAppFileName = path.basename(packagedAppPath);
|
||||
if (process.platform === 'darwin') {
|
||||
const installPrefix = installDir !== '' ? handleTilde(installDir) : path.join(path.sep, 'Applications')
|
||||
const installationDirPath = path.join(installPrefix, packagedAppFileName)
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath)
|
||||
const installPrefix =
|
||||
installDir !== ''
|
||||
? handleTilde(installDir)
|
||||
: path.join(path.sep, 'Applications');
|
||||
const installationDirPath = path.join(installPrefix, packagedAppFileName);
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath);
|
||||
} else if (process.platform === 'win32') {
|
||||
const installPrefix = installDir !== '' ? installDir : process.env.LOCALAPPDATA
|
||||
const installationDirPath = path.join(installPrefix, packagedAppFileName, 'app-dev')
|
||||
const installPrefix =
|
||||
installDir !== '' ? installDir : process.env.LOCALAPPDATA;
|
||||
const installationDirPath = path.join(
|
||||
installPrefix,
|
||||
packagedAppFileName,
|
||||
'app-dev'
|
||||
);
|
||||
try {
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath)
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath);
|
||||
} catch (e) {
|
||||
console.log(`Administrator elevation required to install into "${installationDirPath}"`)
|
||||
const fsAdmin = require('fs-admin')
|
||||
console.log(
|
||||
`Administrator elevation required to install into "${installationDirPath}"`
|
||||
);
|
||||
const fsAdmin = require('fs-admin');
|
||||
return new Promise((resolve, reject) => {
|
||||
fsAdmin.recursiveCopy(packagedAppPath, installationDirPath, (error) => {
|
||||
error ? reject(error) : resolve()
|
||||
})
|
||||
})
|
||||
fsAdmin.recursiveCopy(packagedAppPath, installationDirPath, error => {
|
||||
error ? reject(error) : resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : 'atom-' + CONFIG.channel
|
||||
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : 'apm-' + CONFIG.channel
|
||||
const appName = CONFIG.channel === 'stable' ? 'Atom' : startCase('Atom ' + CONFIG.channel)
|
||||
const appDescription = CONFIG.appMetadata.description
|
||||
const prefixDirPath = installDir !== '' ? handleTilde(installDir) : path.join('/usr', 'local')
|
||||
const shareDirPath = path.join(prefixDirPath, 'share')
|
||||
const installationDirPath = path.join(shareDirPath, atomExecutableName)
|
||||
const applicationsDirPath = path.join(shareDirPath, 'applications')
|
||||
const atomExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'atom' : 'atom-' + CONFIG.channel;
|
||||
const apmExecutableName =
|
||||
CONFIG.channel === 'stable' ? 'apm' : 'apm-' + CONFIG.channel;
|
||||
const appName =
|
||||
CONFIG.channel === 'stable'
|
||||
? 'Atom'
|
||||
: startCase('Atom ' + CONFIG.channel);
|
||||
const appDescription = CONFIG.appMetadata.description;
|
||||
const prefixDirPath =
|
||||
installDir !== '' ? handleTilde(installDir) : path.join('/usr', 'local');
|
||||
const shareDirPath = path.join(prefixDirPath, 'share');
|
||||
const installationDirPath = path.join(shareDirPath, atomExecutableName);
|
||||
const applicationsDirPath = path.join(shareDirPath, 'applications');
|
||||
|
||||
const binDirPath = path.join(prefixDirPath, 'bin')
|
||||
const binDirPath = path.join(prefixDirPath, 'bin');
|
||||
|
||||
fs.mkdirpSync(applicationsDirPath)
|
||||
fs.mkdirpSync(binDirPath)
|
||||
fs.mkdirpSync(applicationsDirPath);
|
||||
fs.mkdirpSync(binDirPath);
|
||||
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath)
|
||||
install(installationDirPath, packagedAppFileName, packagedAppPath);
|
||||
|
||||
{ // Install icons
|
||||
const baseIconThemeDirPath = findBaseIconThemeDirPath()
|
||||
const fullIconName = atomExecutableName + '.png'
|
||||
{
|
||||
// Install icons
|
||||
const baseIconThemeDirPath = findBaseIconThemeDirPath();
|
||||
const fullIconName = atomExecutableName + '.png';
|
||||
|
||||
let existingIconsFound = false
|
||||
let existingIconsFound = false;
|
||||
fs.readdirSync(baseIconThemeDirPath).forEach(size => {
|
||||
const iconPath = path.join(baseIconThemeDirPath, size, 'apps', fullIconName)
|
||||
const iconPath = path.join(
|
||||
baseIconThemeDirPath,
|
||||
size,
|
||||
'apps',
|
||||
fullIconName
|
||||
);
|
||||
if (fs.existsSync(iconPath)) {
|
||||
if (!existingIconsFound) {
|
||||
console.log(`Removing existing icons from "${baseIconThemeDirPath}"`)
|
||||
console.log(
|
||||
`Removing existing icons from "${baseIconThemeDirPath}"`
|
||||
);
|
||||
}
|
||||
existingIconsFound = true
|
||||
fs.removeSync(iconPath)
|
||||
existingIconsFound = true;
|
||||
fs.removeSync(iconPath);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(`Installing icons at "${baseIconThemeDirPath}"`)
|
||||
const appIconsPath = path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png')
|
||||
console.log(`Installing icons at "${baseIconThemeDirPath}"`);
|
||||
const appIconsPath = path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'app-icons',
|
||||
CONFIG.channel,
|
||||
'png'
|
||||
);
|
||||
fs.readdirSync(appIconsPath).forEach(imageName => {
|
||||
if (/\.png$/.test(imageName)) {
|
||||
const size = path.basename(imageName, '.png')
|
||||
const iconPath = path.join(appIconsPath, imageName)
|
||||
fs.copySync(iconPath, path.join(baseIconThemeDirPath, `${size}x${size}`, 'apps', fullIconName))
|
||||
const size = path.basename(imageName, '.png');
|
||||
const iconPath = path.join(appIconsPath, imageName);
|
||||
fs.copySync(
|
||||
iconPath,
|
||||
path.join(
|
||||
baseIconThemeDirPath,
|
||||
`${size}x${size}`,
|
||||
'apps',
|
||||
fullIconName
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(`Updating icon cache for "${baseIconThemeDirPath}"`)
|
||||
console.log(`Updating icon cache for "${baseIconThemeDirPath}"`);
|
||||
try {
|
||||
execSync(`gtk-update-icon-cache ${baseIconThemeDirPath} --force`)
|
||||
execSync(`gtk-update-icon-cache ${baseIconThemeDirPath} --force`);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
{ // Install xdg desktop file
|
||||
const desktopEntryPath = path.join(applicationsDirPath, `${atomExecutableName}.desktop`)
|
||||
{
|
||||
// Install xdg desktop file
|
||||
const desktopEntryPath = path.join(
|
||||
applicationsDirPath,
|
||||
`${atomExecutableName}.desktop`
|
||||
);
|
||||
if (fs.existsSync(desktopEntryPath)) {
|
||||
console.log(`Removing existing desktop entry file at "${desktopEntryPath}"`)
|
||||
fs.removeSync(desktopEntryPath)
|
||||
console.log(
|
||||
`Removing existing desktop entry file at "${desktopEntryPath}"`
|
||||
);
|
||||
fs.removeSync(desktopEntryPath);
|
||||
}
|
||||
console.log(`Writing desktop entry file at "${desktopEntryPath}"`)
|
||||
const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in'))
|
||||
console.log(`Writing desktop entry file at "${desktopEntryPath}"`);
|
||||
const desktopEntryTemplate = fs.readFileSync(
|
||||
path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'resources',
|
||||
'linux',
|
||||
'atom.desktop.in'
|
||||
)
|
||||
);
|
||||
const desktopEntryContents = template(desktopEntryTemplate)({
|
||||
appName,
|
||||
appFileName: atomExecutableName,
|
||||
description: appDescription,
|
||||
installDir: prefixDirPath,
|
||||
iconPath: atomExecutableName
|
||||
})
|
||||
fs.writeFileSync(desktopEntryPath, desktopEntryContents)
|
||||
});
|
||||
fs.writeFileSync(desktopEntryPath, desktopEntryContents);
|
||||
}
|
||||
|
||||
{ // Add atom executable to the PATH
|
||||
const atomBinDestinationPath = path.join(binDirPath, atomExecutableName)
|
||||
{
|
||||
// Add atom executable to the PATH
|
||||
const atomBinDestinationPath = path.join(binDirPath, atomExecutableName);
|
||||
if (fs.existsSync(atomBinDestinationPath)) {
|
||||
console.log(`Removing existing executable at "${atomBinDestinationPath}"`)
|
||||
fs.removeSync(atomBinDestinationPath)
|
||||
console.log(
|
||||
`Removing existing executable at "${atomBinDestinationPath}"`
|
||||
);
|
||||
fs.removeSync(atomBinDestinationPath);
|
||||
}
|
||||
console.log(`Copying atom.sh to "${atomBinDestinationPath}"`)
|
||||
fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), atomBinDestinationPath)
|
||||
console.log(`Copying atom.sh to "${atomBinDestinationPath}"`);
|
||||
fs.copySync(
|
||||
path.join(CONFIG.repositoryRootPath, 'atom.sh'),
|
||||
atomBinDestinationPath
|
||||
);
|
||||
}
|
||||
|
||||
{ // Link apm executable to the PATH
|
||||
const apmBinDestinationPath = path.join(binDirPath, apmExecutableName)
|
||||
{
|
||||
// Link apm executable to the PATH
|
||||
const apmBinDestinationPath = path.join(binDirPath, apmExecutableName);
|
||||
try {
|
||||
fs.lstatSync(apmBinDestinationPath)
|
||||
console.log(`Removing existing executable at "${apmBinDestinationPath}"`)
|
||||
fs.removeSync(apmBinDestinationPath)
|
||||
} catch (e) { }
|
||||
console.log(`Symlinking apm to "${apmBinDestinationPath}"`)
|
||||
fs.symlinkSync(path.join('..', 'share', atomExecutableName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), apmBinDestinationPath)
|
||||
fs.lstatSync(apmBinDestinationPath);
|
||||
console.log(
|
||||
`Removing existing executable at "${apmBinDestinationPath}"`
|
||||
);
|
||||
fs.removeSync(apmBinDestinationPath);
|
||||
} catch (e) {}
|
||||
console.log(`Symlinking apm to "${apmBinDestinationPath}"`);
|
||||
fs.symlinkSync(
|
||||
path.join(
|
||||
'..',
|
||||
'share',
|
||||
atomExecutableName,
|
||||
'resources',
|
||||
'app',
|
||||
'apm',
|
||||
'node_modules',
|
||||
'.bin',
|
||||
'apm'
|
||||
),
|
||||
apmBinDestinationPath
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`Changing permissions to 755 for "${installationDirPath}"`)
|
||||
fs.chmodSync(installationDirPath, '755')
|
||||
console.log(`Changing permissions to 755 for "${installationDirPath}"`);
|
||||
fs.chmodSync(installationDirPath, '755');
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
@ -1,14 +1,14 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const childProcess = require('child_process')
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function (ci) {
|
||||
console.log('Installing script dependencies')
|
||||
module.exports = function(ci) {
|
||||
console.log('Installing script dependencies');
|
||||
childProcess.execFileSync(
|
||||
CONFIG.getNpmBinPath(ci),
|
||||
['--loglevel=error', ci ? 'ci' : 'install'],
|
||||
{env: process.env, cwd: CONFIG.scriptRootPath}
|
||||
)
|
||||
}
|
||||
{ env: process.env, cwd: CONFIG.scriptRootPath }
|
||||
);
|
||||
};
|
||||
|
@ -1,12 +1,14 @@
|
||||
const childProcess = require('child_process')
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const CONFIG = require('../config.js')
|
||||
const CONFIG = require('../config.js');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
if (process.platform === 'win32') {
|
||||
// Use START as a way to ignore error if Atom.exe isnt running
|
||||
childProcess.execSync(`START taskkill /F /IM ${CONFIG.appMetadata.productName}.exe`)
|
||||
childProcess.execSync(
|
||||
`START taskkill /F /IM ${CONFIG.appMetadata.productName}.exe`
|
||||
);
|
||||
} else {
|
||||
childProcess.execSync(`pkill -9 ${CONFIG.appMetadata.productName} || true`)
|
||||
childProcess.execSync(`pkill -9 ${CONFIG.appMetadata.productName} || true`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,27 +1,41 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const coffeelint = require('coffeelint')
|
||||
const expandGlobPaths = require('./expand-glob-paths')
|
||||
const path = require('path')
|
||||
const readFiles = require('./read-files')
|
||||
const coffeelint = require('coffeelint');
|
||||
const expandGlobPaths = require('./expand-glob-paths');
|
||||
const path = require('path');
|
||||
const readFiles = require('./read-files');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
const globPathsToLint = [
|
||||
path.join(CONFIG.repositoryRootPath, 'dot-atom/**/*.coffee'),
|
||||
path.join(CONFIG.repositoryRootPath, 'src/**/*.coffee'),
|
||||
path.join(CONFIG.repositoryRootPath, 'spec/*.coffee')
|
||||
]
|
||||
return expandGlobPaths(globPathsToLint).then(readFiles).then((files) => {
|
||||
const errors = []
|
||||
const lintConfiguration = require(path.join(CONFIG.repositoryRootPath, 'coffeelint.json'))
|
||||
for (let file of files) {
|
||||
const lintErrors = coffeelint.lint(file.content, lintConfiguration, false)
|
||||
for (let error of lintErrors) {
|
||||
errors.push({path: file.path, lineNumber: error.lineNumber, message: error.message, rule: error.rule})
|
||||
];
|
||||
return expandGlobPaths(globPathsToLint)
|
||||
.then(readFiles)
|
||||
.then(files => {
|
||||
const errors = [];
|
||||
const lintConfiguration = require(path.join(
|
||||
CONFIG.repositoryRootPath,
|
||||
'coffeelint.json'
|
||||
));
|
||||
for (let file of files) {
|
||||
const lintErrors = coffeelint.lint(
|
||||
file.content,
|
||||
lintConfiguration,
|
||||
false
|
||||
);
|
||||
for (let error of lintErrors) {
|
||||
errors.push({
|
||||
path: file.path,
|
||||
lineNumber: error.lineNumber,
|
||||
message: error.message,
|
||||
rule: error.rule
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
})
|
||||
}
|
||||
return errors;
|
||||
});
|
||||
};
|
||||
|
@ -1,46 +1,46 @@
|
||||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const path = require('path')
|
||||
const {spawn} = require('child_process')
|
||||
const process = require('process')
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const process = require('process');
|
||||
|
||||
const CONFIG = require('../config')
|
||||
const CONFIG = require('../config');
|
||||
|
||||
module.exports = async function () {
|
||||
module.exports = async function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const eslintArgs = ['--cache', '--format', 'json']
|
||||
const eslintArgs = ['--cache', '--format', 'json'];
|
||||
|
||||
if (process.argv.includes('--fix')) {
|
||||
eslintArgs.push('--fix')
|
||||
eslintArgs.push('--fix');
|
||||
}
|
||||
|
||||
const eslintBinary = process.platform === 'win32' ? 'eslint.cmd' : 'eslint'
|
||||
const eslintBinary = process.platform === 'win32' ? 'eslint.cmd' : 'eslint';
|
||||
const eslint = spawn(
|
||||
path.join('script', 'node_modules', '.bin', eslintBinary),
|
||||
[...eslintArgs, '.'],
|
||||
{ cwd: CONFIG.repositoryRootPath }
|
||||
)
|
||||
);
|
||||
|
||||
let output = ''
|
||||
let errorOutput = ''
|
||||
let output = '';
|
||||
let errorOutput = '';
|
||||
eslint.stdout.on('data', data => {
|
||||
output += data.toString()
|
||||
})
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
eslint.stderr.on('data', data => {
|
||||
errorOutput += data.toString()
|
||||
})
|
||||
errorOutput += data.toString();
|
||||
});
|
||||
|
||||
eslint.on('error', error => reject(error))
|
||||
eslint.on('error', error => reject(error));
|
||||
eslint.on('close', exitCode => {
|
||||
const errors = []
|
||||
let files
|
||||
const errors = [];
|
||||
let files;
|
||||
|
||||
try {
|
||||
files = JSON.parse(output)
|
||||
files = JSON.parse(output);
|
||||
} catch (_) {
|
||||
reject(errorOutput)
|
||||
return
|
||||
reject(errorOutput);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
@ -50,11 +50,11 @@ module.exports = async function () {
|
||||
message: error.message,
|
||||
lineNumber: error.line,
|
||||
rule: error.ruleId
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resolve(errors)
|
||||
})
|
||||
})
|
||||
}
|
||||
resolve(errors);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user