Initial commit for Cordova vscode extension
This commit is contained in:
Родитель
c6616a9bd7
Коммит
ae28f8bf2d
|
@ -1,3 +1,8 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
out/
|
||||
debugger/testCordovaApp/plugins/
|
||||
debugger/testCordovaApp/platforms/
|
||||
debugger/testCordovaApp/.vscode/
|
||||
test/testProject/.vscode
|
||||
test/testProject/platforms
|
|
@ -2,23 +2,37 @@
|
|||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "launch as server",
|
||||
"type": "node",
|
||||
"program": "./out/webkit/webKitDebug.js",
|
||||
"runtimeArgs": ["--harmony"],
|
||||
"name": "Launch Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
|
||||
"stopOnEntry": false,
|
||||
"args": [ "--server=4712" ],
|
||||
"sourceMaps": true,
|
||||
"outDir": "out"
|
||||
"outDir": "out/src",
|
||||
"preLaunchTask": "build"
|
||||
},
|
||||
{
|
||||
"name": "test",
|
||||
"type": "node",
|
||||
"program": "./node_modules/gulp/bin/gulp.js",
|
||||
"name": "Launch Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["test/testProject", "--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
|
||||
"stopOnEntry": false,
|
||||
"args": [ "test" ],
|
||||
"sourceMaps": true,
|
||||
"outDir": "out/test",
|
||||
"preLaunchTask": "build"
|
||||
},
|
||||
{
|
||||
"name": "Launch debugger as server",
|
||||
"type": "node",
|
||||
"program": "./out/webkit/debugCordova.js",
|
||||
"runtimeArgs": ["--harmony"],
|
||||
"stopOnEntry": false,
|
||||
"args": [ "--server=4712" ],
|
||||
"sourceMaps": true,
|
||||
"outDir": "out"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
"bin": true,
|
||||
"out": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,19 @@
|
|||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "build",
|
||||
"args": [],
|
||||
"isBuildCommand": true,
|
||||
"isWatching": false,
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"taskName": "watch-build-test",
|
||||
"isWatching": true,
|
||||
"isTestCommand": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
**/*.map
|
||||
.vscode/**
|
||||
typings/**
|
||||
testapp/**
|
||||
debugger/testapp/**
|
||||
debugger/testCordovaApp/**
|
||||
test/**
|
||||
*.vsix
|
||||
gulpfile.js
|
||||
|
@ -14,4 +15,9 @@ out/test/**
|
|||
|
||||
node_modules/ws/node_modules/bufferutil/**
|
||||
node_modules/ws/node_modules/utf-8-validate/**
|
||||
node_modules/.bin/**
|
||||
node_modules/.bin/**
|
||||
|
||||
out/test/**
|
||||
test/**
|
||||
src/**
|
||||
.gitignore
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
WebKit Debug Adapter for Visual Studio Code
|
||||
Cordova Extension for Visual Studio Code
|
||||
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
## Cordova Extension for Visual Studio Code
|
||||
-------------------
|
||||
## Functionality
|
||||
This extension adds intellisense for Cordova plugins. Additionally, you will be be able to build and run your Cordova application from within the VS Code editor.
|
||||
|
||||
## Install
|
||||
Open up Visual Studio Code, press F1 and type `ext install` and select **Extension for Cordova applications**. Once extension is installed you will
|
||||
be prompted to restart Visual Studio Code.
|
||||
|
||||
## Contributing
|
||||
There are a couple of ways you can contribute to this repo:
|
||||
|
||||
- Ideas, feature requests and bugs: We are open to all ideas and we want to get rid of bugs! Use the Issues section to either report a new issue, provide your ideas or contribute to existing threads
|
||||
- Documentation: Found a typo or strangely worded sentences? Submit a PR!
|
||||
- Code: Contribute bug fixes, features or design changes.
|
||||
|
||||
## Legal
|
||||
Before we can accept your pull request you will need to sign a **Contribution License Agreement**. All you need to do is to submit a pull request, then the PR will get appropriately labelled (e.g. `cla-required`, `cla-norequired`, `cla-signed`, `cla-already-signed`). If you already signed the agreement we will continue with reviewing the PR, otherwise system will tell you how you can sign the CLA. Once you sign the CLA all future PR's will be labeled as `cla-signed`.
|
||||
|
||||
## License
|
||||
[MIT](LICENSE)
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"version": "v4",
|
||||
"repo": "borisyankov/DefinitelyTyped",
|
||||
"ref": "master",
|
||||
"path": "typings",
|
||||
"bundle": "typings/tsd.d.ts"
|
||||
}
|
|
@ -8,7 +8,7 @@ To avoid a conflict, delete the installed extension at `~/.vscode/extensions/msj
|
|||
|
||||
### OS X/Linux
|
||||
* `git clone` this repository
|
||||
* Run `ln -s <path to repo> ~/.vscode/extensions/vsc-webkit`
|
||||
* Run `ln -s <path to repo> ~/.vscode/extensions/vsc-cordova`
|
||||
* You could clone it to the extensions directory if you want, but working with hidden folders in OS X can be a pain.
|
||||
|
||||
### Then...
|
||||
|
@ -24,10 +24,7 @@ In VS Code, run the `launch as server` launch config - it will start the adapter
|
|||
## Testing
|
||||
There is a set of mocha tests which can be run with `gulp test` or with the `test` launch config. Also run `gulp tslint` to check your code against our tslint rules.
|
||||
|
||||
See the project under testapp/ for a bunch of test scenarios crammed onto one page.
|
||||
See the project under testCordovaApp/ for a sample project that should build and compile, and allow debugging of plugins and merges.
|
||||
|
||||
## Code
|
||||
Some files were copied from [the Node adapter](https://github.com/Microsoft/vscode-node-debug) and I'll periodically check for fixes to port over.
|
||||
* Files under common/
|
||||
* adapter/sourceMaps/sourceMaps.ts
|
||||
* adapter/sourceMaps/pathUtilities.ts
|
||||
Much of this code is taken from [the chrome debugging extension](https://github.com/Microsoft/vscode-chrome-debug)
|
|
@ -1,56 +1,39 @@
|
|||
# Debugger for Chrome
|
||||
[![build status](https://travis-ci.org/Microsoft/vscode-chrome-debug.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-node-debug)
|
||||
|
||||
A VS Code extension to debug your JavaScript code in the Chrome browser, or other targets that support the Chrome Debugging Protocol.
|
||||
|
||||
![Screenshot](images/screenshot.png)
|
||||
# Debugger for Cordova
|
||||
A VS Code extension to debug your JavaScript code in Cordova applications.
|
||||
|
||||
## Starting
|
||||
The extension operates in two modes - it can launch an instance of Chrome navigated to your app, or it can attach to a running instance of Chrome. Just like when using the Node debugger, you configure these modes with a `.vscode/launch.json` file in the root directory of your project. You can create this file manually, or Code will create one for you if you try to run your project, and it doesn't exist yet.
|
||||
The extension operates in two modes - it can launch a Cordova app, or it can attach to a running app. Just like when using the Node debugger, you configure these modes with a `.vscode/launch.json` file in the root directory of your project. You can create this file manually, or Code will create one for you if you try to run your project, and it doesn't exist yet.
|
||||
|
||||
To use this extension, you must first open the folder containing the project you want to work on.
|
||||
|
||||
### Launch
|
||||
Two example `launch.json` configs. You must specify either `file` or `url` to launch Chrome against a local file or a url. If you use a url, set `webRoot` to the directory that files are served from. This can be either an absolute path or a path relative to the workspace (the folder open in Code). If `webRoot` isn't set, it defaults to the workspace.
|
||||
Two example `launch.json` configs. You must specify which `platform` to launch or attach to, and it must be either `"android"` or `"ios"`. You may also specify a `target` which is either `"device"` to work with a physical device, `"emulator"` to work with an arbitrary emulator, or a valid option for Cordova's `target` parameter to specify a particular emulator.
|
||||
|
||||
```
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch index.html",
|
||||
"type": "chrome",
|
||||
"name": "Launch app on Android Device",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"file": "index.html"
|
||||
"platform": "android",
|
||||
"target": "device"
|
||||
},
|
||||
{
|
||||
"name": "Launch localhost with sourcemaps",
|
||||
"type": "chrome",
|
||||
"name": "Launch app on iOS emulator for iPhone 4",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"url": "http://localhost/mypage.html",
|
||||
"webRoot": "./app/files",
|
||||
"sourceMaps": true
|
||||
"platform": "ios",
|
||||
"target": "iPhone-4s"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to use Chrome from a different directory, you can also set the "runtimeExecutable" field with a path to the Chrome app.
|
||||
|
||||
### Attach
|
||||
You must launch Chrome with remote debugging enabled in order for the extension to attach to it.
|
||||
|
||||
__Windows__
|
||||
* Right click the Chrome shortcut, and select properties
|
||||
* In the "target" field, append `--remote-debugging-port=9222`
|
||||
* Or in a command prompt, execute `<path to chrome>/chrome.exe --remote-debugging-port=9222`
|
||||
|
||||
__OS X__
|
||||
* In a terminal, execute `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222`
|
||||
|
||||
__Linux__
|
||||
* In a terminal, launch `google-chrome --remote-debugging-port=9222`
|
||||
|
||||
Launch Chrome and navigate to your page.
|
||||
To attach to an already running app, you must specify which platform to target, and whether it is running in an emulator or on a device.
|
||||
|
||||
An example `launch.json` config.
|
||||
```
|
||||
|
@ -58,30 +41,33 @@ An example `launch.json` config.
|
|||
"version": "0.1.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "chrome",
|
||||
"name": "Attach to android emulator",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"port": 9222
|
||||
"platform": "android",
|
||||
"target": "emulator"
|
||||
},
|
||||
{
|
||||
"name": "Attach to url with files served from ./out",
|
||||
"type": "chrome",
|
||||
"name": "Attach to iOS device",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"port": 9222,
|
||||
"webRoot": "out"
|
||||
"platform": "ios",
|
||||
"target": "device"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Other targets
|
||||
You can also theoretically attach to other targets that support the same Chrome remote debugging protocol, such as Electron or Cordova. These aren't officially supported, but should work with basically the same steps. You can use a launch config by setting `"runtimeExecutable"` to a program or script to launch, or an attach config to attach to a process that's already running. If Code can't find the target, you can always verify that it is actually available by navigating to `http://localhost:<port>/json` in a browser. If you get a response with a bunch of JSON, and can find your target page in that JSON, then the target should be available to this extension.
|
||||
|
||||
|
||||
### Other optional launch config fields
|
||||
* diagnosticLogging: When true, the adapter logs its own diagnostic info to the console
|
||||
* runtimeExecutable: Workspace relative or absolute path to the runtime executable to be used. If not specified, Chrome will be used from the default install location
|
||||
* runtimeArgs: Optional arguments passed to the runtime executable
|
||||
* userDataDir: Can be set to a temp directory, then Chrome will use that directory as the user profile directory. If Chrome is already running when you start debugging with a launch config, then the new instance won't start in remote debugging mode. If you don't want to close the original instance, you can set this property and the new instance will correctly be in remote debugging mode.
|
||||
* port: A port to use for proxying to the device/emulator. Defaults to 9222
|
||||
* sourceMaps: a boolean value specifying whether to use javascript sourcemaps if they exist. Defaults to false.
|
||||
* webkitRangeMin, webkitRangeMax: two numbers, specifying a port range to use for iOS. Devices and the simulator will use ports within this range to proxy through to the specific device/simulator. Defaults to 9223-9322
|
||||
* attachAttempts: Number of attempts to make when attaching to an iOS app. Launching to the iOS simulator or a device can have a significant delay before the app is debuggable. Defaults to 5
|
||||
* attachDelay: Delay in milliseconds between attach attempts for iOS. Defaults to 1000
|
||||
* iosDebugProxyPort: Port to use when launching iOS applications on a device. Defaults to 9221
|
||||
* appStepLaunchTimeout: Timeout in milliseconds for individual steps when launching an iOS app on a device. Defaults to 5000
|
||||
|
||||
## Usage
|
||||
When your launch config is set up, you can debug your project! Pick a launch config from the dropdown on the Debug pane in Code. Press the play button or F5 to start.
|
||||
|
@ -100,10 +86,7 @@ When your launch config is set up, you can debug your project! Pick a launch con
|
|||
|
||||
## Troubleshooting
|
||||
General things to try if you're having issues:
|
||||
* Ensure `webRoot` is set correctly if needed
|
||||
* If sourcemaps are enabled, ensure `sourceRoot` is an absolute path
|
||||
* Close other running instances of Chrome - if Chrome is already running, the extension may not be able to attach, when using launch mode. Chrome can even stay running in the background when all its windows are closed, which will interfere.
|
||||
* Ensure nothing else is using port 9222, or specify a different port in your launch config
|
||||
* Check the console for warnings that this extension prints in some cases when it can't attach
|
||||
* Ensure the code in Chrome matches the code in Code. Chrome may cache an old version.
|
||||
* File a bug in this extension's [GitHub repo](https://github.com/Microsoft/vscode-webkit-debug). Set the "diagnosticLogging" field in your launch config and attach the logs when filing a bug.
|
||||
|
|
|
@ -38,7 +38,7 @@ export class AdapterProxy {
|
|||
.filter(transformer => request.command in transformer)
|
||||
.reduce(
|
||||
(p, transformer) => p.then(() => transformer[request.command](request.arguments, request.seq)),
|
||||
Promise.resolve<void>());
|
||||
Promise.resolve<void>(void 0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,7 +46,7 @@ export class AdapterProxy {
|
|||
*/
|
||||
private transformResponse(request: DebugProtocol.Request, body: any): Promise<void> {
|
||||
if (!body) {
|
||||
return Promise.resolve<void>();
|
||||
return Promise.resolve<void>(void 0);
|
||||
}
|
||||
|
||||
const bodyTransformMethodName = request.command + 'Response';
|
||||
|
@ -56,7 +56,7 @@ export class AdapterProxy {
|
|||
.filter(transformer => bodyTransformMethodName in transformer)
|
||||
.reduce(
|
||||
(p, transformer) => p.then(() => transformer[bodyTransformMethodName](body, request.seq)),
|
||||
Promise.resolve<void>());
|
||||
Promise.resolve<void>(void 0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -183,13 +183,13 @@ export class SourceMapTransformer implements IDebugTransformer {
|
|||
|
||||
public scriptParsed(event: DebugProtocol.Event): void {
|
||||
if (this._sourceMaps) {
|
||||
this._allRuntimeScriptPaths.add(event.body.scriptUrl);
|
||||
|
||||
if (!event.body.sourceMapURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._sourceMaps.ProcessNewSourceMap(event.body.scriptUrl, event.body.sourceMapURL).then(() => {
|
||||
this._allRuntimeScriptPaths.add(event.body.scriptUrl);
|
||||
|
||||
const sources = this._sourceMaps.AllMappedSources(event.body.scriptUrl);
|
||||
if (sources) {
|
||||
utils.Logger.log(`SourceMaps.scriptParsed: ${event.body.scriptUrl} was just loaded and has mapped sources: ${JSON.stringify(sources) }`);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
interface ICordovaLaunchRequestArgs extends DebugProtocol.LaunchRequestArguments, ICordovaAttachRequestArgs {
|
||||
iosDebugProxyPort?: number;
|
||||
appStepLaunchTimeout?: number;
|
||||
}
|
||||
|
||||
interface ICordovaAttachRequestArgs extends DebugProtocol.AttachRequestArguments, IAttachRequestArgs {
|
||||
cwd: string; /* Automatically set by VS Code to the currently opened folder */
|
||||
platform: string;
|
||||
target?: string;
|
||||
webkitRangeMin?: number;
|
||||
webkitRangeMax?: number;
|
||||
attachAttempts?: number;
|
||||
attachDelay?: number;
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as elementtree from 'elementtree';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as Q from 'q';
|
||||
import * as request from 'request';
|
||||
|
||||
import {CordovaIosDeviceLauncher} from './cordovaIosDeviceLauncher';
|
||||
|
||||
import {cordovaRunCommand, execCommand} from './extension';
|
||||
|
||||
import {WebKitDebugAdapter} from '../webkit/WebKitDebugAdapter';
|
||||
|
||||
import {CordovaProjectHelper} from '../../src/utils/cordovaProjectHelper';
|
||||
|
||||
export class CordovaDebugAdapter extends WebKitDebugAdapter {
|
||||
private outputLogger: (message: string, error?: boolean) => void;
|
||||
|
||||
public constructor(outputLogger: (message: string, error?: boolean) => void) {
|
||||
super();
|
||||
this.outputLogger = outputLogger;
|
||||
}
|
||||
|
||||
public launch(launchArgs: ICordovaLaunchRequestArgs): Promise<void> {
|
||||
launchArgs.port = launchArgs.port || 9222;
|
||||
launchArgs.target = launchArgs.target || 'emulator';
|
||||
launchArgs.cwd = CordovaProjectHelper.getCordovaProjectRoot(launchArgs.cwd);
|
||||
let platform = launchArgs.platform && launchArgs.platform.toLowerCase();
|
||||
return new Promise<void>((resolve, reject) => Q({}).then(() => {
|
||||
this.outputLogger(`Launching for ${platform} (This may take a while)...`);
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return this.launchAndroid(launchArgs);
|
||||
case 'ios':
|
||||
return this.launchIos(launchArgs);
|
||||
default:
|
||||
throw new Error(`Unknown Platform: ${platform}`);
|
||||
}
|
||||
}).then(() => {
|
||||
return this.attach(launchArgs);
|
||||
}).done(resolve, reject));
|
||||
}
|
||||
|
||||
public attach(attachArgs: ICordovaAttachRequestArgs): Promise<void> {
|
||||
attachArgs.port = attachArgs.port || 9222;
|
||||
attachArgs.target = attachArgs.target || 'emulator';
|
||||
attachArgs.cwd = CordovaProjectHelper.getCordovaProjectRoot(attachArgs.cwd);
|
||||
let platform = attachArgs.platform && attachArgs.platform.toLowerCase();
|
||||
return new Promise<void>((resolve, reject) => Q({}).then(() => {
|
||||
this.outputLogger(`Attaching to ${platform}`);
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return this.attachAndroid(attachArgs);
|
||||
case 'ios':
|
||||
return this.attachIos(attachArgs);
|
||||
default:
|
||||
throw new Error(`Unknown Platform: ${platform}`);
|
||||
}
|
||||
}).then((processedAttachArgs: IAttachRequestArgs & {url?: string}) => {
|
||||
this.outputLogger('Attaching to app.');
|
||||
this.outputLogger('', true); // Send blank message on stderr to include a divider between prelude and app starting
|
||||
return super.attach(processedAttachArgs, processedAttachArgs.url);
|
||||
}).done(resolve, reject));
|
||||
}
|
||||
|
||||
private launchAndroid(launchArgs: ICordovaLaunchRequestArgs): Q.Promise<void> {
|
||||
let workingDirectory = launchArgs.cwd;
|
||||
let errorLogger = (message) => this.outputLogger(message, true);
|
||||
// Launch the app
|
||||
let isDevice = launchArgs.target.toLowerCase() === 'device';
|
||||
let args = ['run', 'android', isDevice ? '--device' : '--emulator', '--verbose'];
|
||||
if (['device', 'emulator'].indexOf(launchArgs.target.toLowerCase()) === -1) {
|
||||
args.push(`--target=${launchArgs.target}`);
|
||||
}
|
||||
let cordovaResult = cordovaRunCommand(args, errorLogger, workingDirectory).then((output) => {
|
||||
let runOutput = output[0];
|
||||
let stderr = output[1];
|
||||
let errorMatch = /(ERROR.*)/.exec(runOutput);
|
||||
if (errorMatch) {
|
||||
errorLogger(runOutput);
|
||||
errorLogger(stderr);
|
||||
throw new Error(`Error running android`);
|
||||
}
|
||||
this.outputLogger('App successfully launched');
|
||||
});
|
||||
|
||||
return cordovaResult;
|
||||
}
|
||||
|
||||
private attachAndroid(attachArgs: ICordovaAttachRequestArgs): Q.Promise<IAttachRequestArgs> {
|
||||
let errorLogger = (message) => this.outputLogger(message, true);
|
||||
// Determine which device/emulator we are targeting
|
||||
|
||||
let adbDevicesResult = execCommand('adb devices', errorLogger).then((devicesOutput) => {
|
||||
if (attachArgs.target.toLowerCase() === 'device') {
|
||||
let deviceMatch = /\n([^\t]+)\tdevice($|\n)/m.exec(devicesOutput.replace(/\r/g, ''));
|
||||
if (!deviceMatch) {
|
||||
errorLogger(devicesOutput);
|
||||
throw new Error('Unable to find device');
|
||||
}
|
||||
return deviceMatch[1];
|
||||
} else {
|
||||
let emulatorMatch = /\n(emulator[^\t]+)\tdevice($|\n)/m.exec(devicesOutput.replace(/\r/g, ''));
|
||||
if (!emulatorMatch) {
|
||||
errorLogger(devicesOutput);
|
||||
throw new Error('Unable to find emulator');
|
||||
}
|
||||
return emulatorMatch[1];
|
||||
}
|
||||
});
|
||||
let packagePromise = Q.nfcall(fs.readFile, path.join(attachArgs.cwd, 'platforms', 'android', 'AndroidManifest.xml')).then((manifestContents) => {
|
||||
let parsedFile = elementtree.XML(manifestContents.toString());
|
||||
let packageKey = 'package';
|
||||
return parsedFile.attrib[packageKey];
|
||||
});
|
||||
return Q.all([packagePromise, adbDevicesResult]).spread((appPackageName, targetDevice) => {
|
||||
let getPidCommand = `adb -s ${targetDevice} shell "ps | grep ${appPackageName}"`;
|
||||
|
||||
let findPidFunction = () => execCommand(getPidCommand, errorLogger).then((pidLine: string) => /^[^ ]+ +([^ ]+) /m.exec(pidLine));
|
||||
|
||||
return CordovaDebugAdapter.retryAsync(findPidFunction, (match) => !!match, 5, 1, 5000, 'Unable to find pid of cordova app').then((match: RegExpExecArray) => match[1]).then((pid) => {
|
||||
// Configure port forwarding to the app
|
||||
let forwardSocketCommand = `adb -s ${targetDevice} forward tcp:${attachArgs.port} localabstract:webview_devtools_remote_${pid}`;
|
||||
this.outputLogger('Forwarding debug port');
|
||||
return execCommand(forwardSocketCommand, errorLogger);
|
||||
});
|
||||
}).then(() => {
|
||||
let args: IAttachRequestArgs = {port: attachArgs.port, webRoot: attachArgs.cwd, cwd: attachArgs.cwd };
|
||||
return args;
|
||||
});
|
||||
}
|
||||
|
||||
private launchIos(launchArgs: ICordovaLaunchRequestArgs): Q.Promise<void> {
|
||||
if (os.platform() !== 'darwin') {
|
||||
return Q.reject<void>('Unable to launch iOS on non-mac machines');
|
||||
}
|
||||
let workingDirectory = launchArgs.cwd;
|
||||
let errorLogger = (message) => this.outputLogger(message, true);
|
||||
this.outputLogger('Launching app (This may take a while)...');
|
||||
|
||||
let iosDebugProxyPort = launchArgs.iosDebugProxyPort || 9221;
|
||||
let appStepLaunchTimeout = launchArgs.appStepLaunchTimeout || 5000;
|
||||
// Launch the app
|
||||
if (launchArgs.target.toLowerCase() === 'device') {
|
||||
// cordova run ios does not terminate, so we do not know when to try and attach.
|
||||
// Instead, we try to launch manually using homebrew.
|
||||
return cordovaRunCommand(['build', 'ios', '--device'], errorLogger, workingDirectory).then(() => {
|
||||
let buildFolder = path.join(workingDirectory, 'platforms', 'ios', 'build', 'device');
|
||||
|
||||
this.outputLogger('Installing app on device');
|
||||
|
||||
let installPromise = Q.nfcall(fs.readdir, buildFolder).then((files: string[]) => {
|
||||
let ipaFiles = files.filter((file) => /\.ipa$/.test(file));
|
||||
if (ipaFiles.length === 0) {
|
||||
throw new Error('Unable to find an ipa to install');
|
||||
}
|
||||
let ipaFile = path.join(buildFolder, ipaFiles[0]);
|
||||
|
||||
return execCommand(`ideviceinstaller -i '${ipaFile}'`, errorLogger);
|
||||
});
|
||||
|
||||
return Q.all([CordovaIosDeviceLauncher.getBundleIdentifier(workingDirectory), installPromise]);
|
||||
}).spread((appBundleId) => {
|
||||
// App is now built and installed. Try to launch
|
||||
this.outputLogger('Launching app');
|
||||
return CordovaIosDeviceLauncher.startDebugProxy(iosDebugProxyPort).then(() => {
|
||||
return CordovaIosDeviceLauncher.startApp(appBundleId, iosDebugProxyPort, appStepLaunchTimeout);
|
||||
});
|
||||
}).then(() => void(0));
|
||||
} else {
|
||||
let target = launchArgs.target.toLowerCase() === 'emulator' ? null : launchArgs.target;
|
||||
if (target) {
|
||||
return cordovaRunCommand(['emulate', 'ios', '--target=' + target], errorLogger, workingDirectory).catch((err) => {
|
||||
return cordovaRunCommand(['emulate', 'ios', '--list'], errorLogger, workingDirectory).then((output) => {
|
||||
// List out available targets
|
||||
errorLogger('Unable to run with given target.');
|
||||
errorLogger(output[0].replace(/\*+[^*]+\*+/g, '')); // Print out list of targets, without ** RUN SUCCEEDED **
|
||||
throw err;
|
||||
});
|
||||
}).then(() => void(0));
|
||||
} else {
|
||||
return cordovaRunCommand(['emulate', 'ios'], errorLogger, workingDirectory).then(() => void(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private attachIos(attachArgs: ICordovaAttachRequestArgs): Q.Promise<IAttachRequestArgs> {
|
||||
attachArgs.webkitRangeMin = attachArgs.webkitRangeMin || 9223;
|
||||
attachArgs.webkitRangeMax = attachArgs.webkitRangeMax || 9322;
|
||||
attachArgs.attachAttempts = attachArgs.attachAttempts || 5;
|
||||
attachArgs.attachDelay = attachArgs.attachDelay || 1000;
|
||||
// Start the tunnel through to the webkit debugger on the device
|
||||
this.outputLogger('Configuring debugging proxy');
|
||||
return CordovaIosDeviceLauncher.startWebkitDebugProxy(attachArgs.port, attachArgs.webkitRangeMin, attachArgs.webkitRangeMax).then(() => {
|
||||
if (attachArgs.target.toLowerCase() === 'device') {
|
||||
return CordovaIosDeviceLauncher.getBundleIdentifier(attachArgs.cwd).then(CordovaIosDeviceLauncher.getPathOnDevice);
|
||||
} else {
|
||||
return Q.nfcall(fs.readdir, path.join(attachArgs.cwd, 'platforms', 'ios', 'build', 'emulator')).then((entries: string[]) => {
|
||||
let filtered = entries.filter((entry) => /\.app$/.test(entry));
|
||||
if (filtered.length > 0) {
|
||||
return filtered[0];
|
||||
} else {
|
||||
throw new Error('Unable to find .app file');
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then((packagePath) => {
|
||||
let devicePortDeferred = Q.defer<number>();
|
||||
request.get(`http://localhost:${attachArgs.port}/json`, (err, response, body) => {
|
||||
if (err) {
|
||||
this.outputLogger('Unable to communicate with ios_webkit_debug_proxy');
|
||||
devicePortDeferred.reject(err);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let endpointsList = JSON.parse(body);
|
||||
let devices = endpointsList.filter((entry) =>
|
||||
attachArgs.target.toLowerCase() === 'device' ? entry.deviceId !== 'SIMULATOR'
|
||||
: entry.deviceId === 'SIMULATOR'
|
||||
);
|
||||
let device = devices[0];
|
||||
// device.url is of the form 'localhost:port'
|
||||
devicePortDeferred.resolve(parseInt(device.url.split(':')[1], 10));
|
||||
} catch (e) {
|
||||
devicePortDeferred.reject('Unable to communicate with ios_webkit_debug_proxy');
|
||||
}
|
||||
});
|
||||
|
||||
let findWebviewFunc = (appPort) => {
|
||||
return () => {
|
||||
let deferred = Q.defer<any[]>();
|
||||
request.get(`http://localhost:${appPort}/json`, (err, response, body) => {
|
||||
if (err) {
|
||||
this.outputLogger('Unable to communicate with device');
|
||||
deferred.reject(err);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let webviewsList = JSON.parse(body);
|
||||
deferred.resolve(webviewsList.filter((entry) => entry.url.indexOf(packagePath) !== -1));
|
||||
} catch (e) {
|
||||
deferred.reject('Unable to communicate with device');
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
};
|
||||
};
|
||||
|
||||
return devicePortDeferred.promise.then((appPort) => {
|
||||
return CordovaDebugAdapter.retryAsync(findWebviewFunc(appPort), (webviewList) => webviewList.length > 0, attachArgs.attachAttempts, 1, attachArgs.attachDelay, 'Unable to find webview').then((relevantViews) => {
|
||||
return {port: appPort, url: relevantViews[0].url};
|
||||
});
|
||||
}).then(({port, url}) => {
|
||||
return {port: port, webRoot: attachArgs.cwd, cwd: attachArgs.cwd, url: url};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static retryAsync<T>(func: () => Q.Promise<T>, condition: (result: T) => boolean, maxRetries: number, iteration: number, delay: number, failure: string): Q.Promise<T> {
|
||||
return func().then(result => {
|
||||
if (condition(result)) {
|
||||
return result;
|
||||
} else {
|
||||
if (iteration < maxRetries) {
|
||||
return Q.delay(delay).then(() => CordovaDebugAdapter.retryAsync(func, condition, maxRetries, iteration + 1, delay, failure));
|
||||
} else {
|
||||
throw new Error(failure);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import {Response} from '../common/v8Protocol';
|
||||
import {DebugSession, ErrorDestination, OutputEvent} from '../common/debugSession';
|
||||
import {CordovaDebugAdapter} from './cordovaDebugAdapter';
|
||||
import {CordovaPathTransformer} from './cordovaPathTransformer';
|
||||
import {Logger} from '../webkit/utilities';
|
||||
|
||||
import {AdapterProxy} from '../adapter/adapterProxy';
|
||||
import {LineNumberTransformer} from '../adapter/lineNumberTransformer';
|
||||
|
||||
import {SourceMapTransformer} from '../adapter/sourceMaps/sourceMapTransformer';
|
||||
import * as Q from 'q';
|
||||
|
||||
export class CordovaDebugSession extends DebugSession {
|
||||
private _adapterProxy: AdapterProxy;
|
||||
|
||||
protected launchRequest(response: DebugProtocol.LaunchResponse, args: DebugProtocol.LaunchRequestArguments): Q.Promise<any> {
|
||||
Logger.log(`Cordova launchRequest: ${response}(${JSON.stringify(args)})`);
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
protected attachRequest(response: DebugProtocol.AttachResponse, args: DebugProtocol.AttachRequestArguments): Q.Promise<any> {
|
||||
Logger.log(`Cordova attachRequest: ${response}(${JSON.stringify(args)})`);
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): Q.Promise<any> {
|
||||
Logger.log(`Cordova initializeRequest: ${response}(${JSON.stringify(args)})`);
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
public constructor(targetLinesStartAt1: boolean, isServer: boolean = false) {
|
||||
super(targetLinesStartAt1, isServer);
|
||||
|
||||
let outputLogger = (message: string, error: boolean = false) => this.sendEvent(new OutputEvent(message + '\n', error ? 'stderr' : 'console'));
|
||||
|
||||
this._adapterProxy = new AdapterProxy(
|
||||
[
|
||||
new LineNumberTransformer(targetLinesStartAt1),
|
||||
new SourceMapTransformer(),
|
||||
new CordovaPathTransformer(outputLogger)
|
||||
],
|
||||
new CordovaDebugAdapter(outputLogger),
|
||||
event => this.sendEvent(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a response and a promise to the response body. If the promise is successful, assigns the response body and sends the response.
|
||||
* If the promise fails, sets the appropriate response parameters and sends the response.
|
||||
*/
|
||||
private sendResponseAsync(request: DebugProtocol.Request, response: DebugProtocol.Response, responseP: Promise<any>): void {
|
||||
responseP.then(
|
||||
(body?) => {
|
||||
response.body = body;
|
||||
this.sendResponse(response);
|
||||
},
|
||||
e => {
|
||||
const eStr = e ? e.toString() : 'Unknown error';
|
||||
if (eStr === 'unknowncommand') {
|
||||
this.sendErrorResponse(response, 1014, 'Unrecognized request', null, ErrorDestination.Telemetry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.command === 'evaluate') {
|
||||
// Errors from evaluate show up in the console or watches pane. Doesn't seem right
|
||||
// as it's not really a failed request. So it doesn't need the tag and worth special casing.
|
||||
response.message = e.toString();
|
||||
} else {
|
||||
// These errors show up in the message bar at the top (or nowhere), sometimes not obvious that they
|
||||
// come from the adapter
|
||||
response.message = '[cordova-debug-adapter] ' + e.toString();
|
||||
Logger.log('Error: ' + e.toString());
|
||||
}
|
||||
|
||||
response.success = false;
|
||||
this.sendResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload dispatchRequest to dispatch to the adapter proxy instead of debugSession's methods for each request.
|
||||
*/
|
||||
protected dispatchRequest(request: DebugProtocol.Request): void {
|
||||
const response = new Response(request);
|
||||
try {
|
||||
let cordovaCommandResult = Q({});
|
||||
if (request.command === 'initialize') {
|
||||
let args = <DebugProtocol.InitializeRequestArguments> request.arguments;
|
||||
cordovaCommandResult = this.initializeRequest(<DebugProtocol.InitializeResponse> response, args);
|
||||
|
||||
} else if (request.command === 'launch') {
|
||||
cordovaCommandResult = this.launchRequest(<DebugProtocol.LaunchResponse> response, request.arguments);
|
||||
|
||||
} else if (request.command === 'attach') {
|
||||
cordovaCommandResult = this.attachRequest(<DebugProtocol.AttachResponse> response, request.arguments);
|
||||
}
|
||||
cordovaCommandResult.done(() => {
|
||||
Logger.log(`From client: ${request.command}(${JSON.stringify(request.arguments) })`);
|
||||
this.sendResponseAsync(
|
||||
request,
|
||||
response,
|
||||
this._adapterProxy.dispatchRequest(request));
|
||||
});
|
||||
} catch (e) {
|
||||
this.sendErrorResponse(response, 1104, 'Exception while processing request (exception: {_exception})', { _exception: e.message }, ErrorDestination.Telemetry);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import child_process = require ('child_process');
|
||||
import fs = require ('fs');
|
||||
import net = require ('net');
|
||||
import path = require('path');
|
||||
import pl = require ('plist-with-patches');
|
||||
import Q = require ('q');
|
||||
|
||||
let promiseExec = Q.denodeify(child_process.exec);
|
||||
|
||||
export class CordovaIosDeviceLauncher {
|
||||
private static nativeDebuggerProxyInstance: child_process.ChildProcess;
|
||||
private static webDebuggerProxyInstance: child_process.ChildProcess;
|
||||
|
||||
public static getBundleIdentifier(projectRoot: string): Q.Promise<string> {
|
||||
return Q.nfcall(fs.readdir, path.join(projectRoot, 'platforms', 'ios')).then((files: string[]) => {
|
||||
let xcodeprojfiles = files.filter((file: string) => /\.xcodeproj$/.test(file));
|
||||
if (xcodeprojfiles.length === 0) {
|
||||
throw new Error('Unable to find xcodeproj file');
|
||||
}
|
||||
let xcodeprojfile = xcodeprojfiles[0];
|
||||
let projectName = /^(.*)\.xcodeproj/.exec(xcodeprojfile)[1];
|
||||
let plist = pl.parseFileSync(path.join(projectRoot, 'platforms', 'ios', projectName, projectName + '-Info.plist'));
|
||||
return plist.CFBundleIdentifier;
|
||||
});
|
||||
}
|
||||
|
||||
public static startDebugProxy(proxyPort: number): Q.Promise<child_process.ChildProcess> {
|
||||
if (CordovaIosDeviceLauncher.nativeDebuggerProxyInstance) {
|
||||
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.kill('SIGHUP'); // idevicedebugserver does not exit from SIGTERM
|
||||
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = null;
|
||||
}
|
||||
|
||||
return CordovaIosDeviceLauncher.mountDeveloperImage().then(function (): Q.Promise<child_process.ChildProcess> {
|
||||
let deferred = Q.defer<child_process.ChildProcess>();
|
||||
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance = child_process.spawn('idevicedebugserverproxy', [proxyPort.toString()]);
|
||||
CordovaIosDeviceLauncher.nativeDebuggerProxyInstance.on('error', function (err: any): void {
|
||||
deferred.reject(err);
|
||||
});
|
||||
// Allow 200ms for the spawn to error out, ~125ms isn't uncommon for some failures
|
||||
Q.delay(200).then(() => deferred.resolve(CordovaIosDeviceLauncher.nativeDebuggerProxyInstance));
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
public static startWebkitDebugProxy(proxyPort: number, proxyRangeStart: number, proxyRangeEnd: number): Q.Promise<any> {
|
||||
if (CordovaIosDeviceLauncher.webDebuggerProxyInstance) {
|
||||
CordovaIosDeviceLauncher.webDebuggerProxyInstance.kill();
|
||||
CordovaIosDeviceLauncher.webDebuggerProxyInstance = null;
|
||||
}
|
||||
|
||||
let deferred = Q.defer();
|
||||
let portRange = `null:${proxyPort},:${proxyRangeStart}-${proxyRangeEnd}`;
|
||||
CordovaIosDeviceLauncher.webDebuggerProxyInstance = child_process.spawn('ios_webkit_debug_proxy', ['-c', portRange]);
|
||||
CordovaIosDeviceLauncher.webDebuggerProxyInstance.on('error', function (err: Error) {
|
||||
deferred.reject(new Error('Unable to start ios_webkit_debug_proxy.'));
|
||||
});
|
||||
// Allow some time for the spawned process to error out
|
||||
Q.delay(250).then(() => deferred.resolve({}));
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Attempt to start the app on the device, using the debug server proxy on a given port.
|
||||
// Returns a socket speaking remote gdb protocol with the debug server proxy.
|
||||
public static startApp(packageId: string, proxyPort: number, appLaunchStepTimeout: number): Q.Promise<string> {
|
||||
// When a user has many apps installed on their device, the response from ideviceinstaller may be large (500k or more)
|
||||
// This exceeds the maximum stdout size that exec allows, so we redirect to a temp file.
|
||||
return CordovaIosDeviceLauncher.getPathOnDevice(packageId).then(function (path: string): Q.Promise<string> { return CordovaIosDeviceLauncher.startAppViaDebugger(proxyPort, path, appLaunchStepTimeout); });
|
||||
}
|
||||
|
||||
public static getPathOnDevice(packageId: string): Q.Promise<string> {
|
||||
return promiseExec('ideviceinstaller -l -o xml > /tmp/$$.ideviceinstaller && echo /tmp/$$.ideviceinstaller')
|
||||
.catch(function (err: any): any {
|
||||
if (err.code === 'ENOENT') {
|
||||
throw new Error('Unable to find ideviceinstaller.');
|
||||
}
|
||||
throw err;
|
||||
}).spread<string>(function (stdout: string, stderr: string): string {
|
||||
// First find the path of the app on the device
|
||||
let filename: string = stdout.trim();
|
||||
if (!/^\/tmp\/[0-9]+\.ideviceinstaller$/.test(filename)) {
|
||||
throw new Error('Unable to list installed applications on device');
|
||||
}
|
||||
|
||||
let list: any[] = pl.parseFileSync(filename);
|
||||
fs.unlink(filename);
|
||||
for (let i: number = 0; i < list.length; ++i) {
|
||||
if (list[i].CFBundleIdentifier === packageId) {
|
||||
let path: string = list[i].Path;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Application not installed on the device');
|
||||
});
|
||||
}
|
||||
|
||||
public static startAppViaDebugger(portNumber: number, packagePath: string, appLaunchStepTimeout: number): Q.Promise<string> {
|
||||
let encodedPath: string = CordovaIosDeviceLauncher.encodePath(packagePath);
|
||||
|
||||
// We need to send 3 messages to the proxy, waiting for responses between each message:
|
||||
// A(length of encoded path),0,(encoded path)
|
||||
// Hc0
|
||||
// c
|
||||
// We expect a '+' for each message sent, followed by a $OK#9a to indicate that everything has worked.
|
||||
// For more info, see http://www.opensource.apple.com/source/lldb/lldb-167.2/docs/lldb-gdb-remote.txt
|
||||
let socket: net.Socket = new net.Socket();
|
||||
let initState: number = 0;
|
||||
let endStatus: number = null;
|
||||
let endSignal: number = null;
|
||||
|
||||
let deferred1: Q.Deferred<net.Socket> = Q.defer<net.Socket>();
|
||||
let deferred2: Q.Deferred<net.Socket> = Q.defer<net.Socket>();
|
||||
let deferred3: Q.Deferred<net.Socket> = Q.defer<net.Socket>();
|
||||
|
||||
socket.on('data', function (data: any): void {
|
||||
data = data.toString();
|
||||
while (data[0] === '+') { data = data.substring(1); }
|
||||
// Acknowledge any packets sent our way
|
||||
if (data[0] === '$') {
|
||||
socket.write('+');
|
||||
if (data[1] === 'W') {
|
||||
// The app process has exited, with hex status given by data[2-3]
|
||||
let status: number = parseInt(data.substring(2, 4), 16);
|
||||
endStatus = status;
|
||||
socket.end();
|
||||
} else if (data[1] === 'X') {
|
||||
// The app rocess exited because of signal given by data[2-3]
|
||||
let signal: number = parseInt(data.substring(2, 4), 16);
|
||||
endSignal = signal;
|
||||
socket.end();
|
||||
} else if (data.substring(1, 3) === 'OK') {
|
||||
// last command was received OK;
|
||||
if (initState === 1) {
|
||||
deferred1.resolve(socket);
|
||||
} else if (initState === 2) {
|
||||
deferred2.resolve(socket);
|
||||
}
|
||||
} else if (data[1] === 'O') {
|
||||
// STDOUT was written to, and the rest of the input until reaching a '#' is a hex-encoded string of that output
|
||||
if (initState === 3) {
|
||||
deferred3.resolve(socket);
|
||||
initState++;
|
||||
}
|
||||
} else if (data[1] === 'E') {
|
||||
// An error has occurred, with error code given by data[2-3]: parseInt(data.substring(2, 4), 16)
|
||||
deferred1.reject('Unable to launch application.');
|
||||
deferred2.reject('Unable to launch application.');
|
||||
deferred3.reject('Unable to launch application.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('end', function (): void {
|
||||
deferred1.reject('Unable to launch application.');
|
||||
deferred2.reject('Unable to launch application.');
|
||||
deferred3.reject('Unable to launch application.');
|
||||
});
|
||||
|
||||
socket.on('error', function (err: Error): void {
|
||||
deferred1.reject(err);
|
||||
deferred2.reject(err);
|
||||
deferred3.reject(err);
|
||||
});
|
||||
|
||||
socket.connect(portNumber, 'localhost', function (): void {
|
||||
// set argument 0 to the (encoded) path of the app
|
||||
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('A' + encodedPath.length + ',0,' + encodedPath);
|
||||
initState++;
|
||||
socket.write(cmd);
|
||||
setTimeout(function (): void {
|
||||
deferred1.reject('Timeout launching application. Is the device locked?');
|
||||
}, appLaunchStepTimeout);
|
||||
});
|
||||
|
||||
return deferred1.promise.then(function (sock: net.Socket): Q.Promise<net.Socket> {
|
||||
// Set the step and continue thread to any thread
|
||||
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('Hc0');
|
||||
initState++;
|
||||
sock.write(cmd);
|
||||
setTimeout(function (): void {
|
||||
deferred2.reject('Timeout launching application. Is the device locked?');
|
||||
}, appLaunchStepTimeout);
|
||||
return deferred2.promise;
|
||||
}).then(function (sock: net.Socket): Q.Promise<net.Socket> {
|
||||
// Continue execution; actually start the app running.
|
||||
let cmd: string = CordovaIosDeviceLauncher.makeGdbCommand('c');
|
||||
initState++;
|
||||
sock.write(cmd);
|
||||
setTimeout(function (): void {
|
||||
deferred3.reject('Timeout launching application. Is the device locked?');
|
||||
}, appLaunchStepTimeout);
|
||||
return deferred3.promise;
|
||||
}).then(() => packagePath);
|
||||
}
|
||||
|
||||
public static encodePath(packagePath: string): string {
|
||||
// Encode the path by converting each character value to hex
|
||||
return packagePath.split('').map((c: string) => c.charCodeAt(0).toString(16)).join('').toUpperCase();
|
||||
}
|
||||
|
||||
private static mountDeveloperImage(): Q.Promise<any> {
|
||||
return CordovaIosDeviceLauncher.getDiskImage()
|
||||
.then(function (path: string): Q.Promise<any> {
|
||||
let imagemounter: child_process.ChildProcess = child_process.spawn('ideviceimagemounter', [path]);
|
||||
let deferred: Q.Deferred<any> = Q.defer();
|
||||
let stdout: string = '';
|
||||
imagemounter.stdout.on('data', function (data: any): void {
|
||||
stdout += data.toString();
|
||||
});
|
||||
imagemounter.on('close', function (code: number): void {
|
||||
if (code !== 0) {
|
||||
if (stdout.indexOf('Error:') !== -1) {
|
||||
deferred.resolve({}); // Technically failed, but likely caused by the image already being mounted.
|
||||
} else if (stdout.indexOf('No device found, is it plugged in?') !== -1) {
|
||||
deferred.reject('Unable to find device. Is the device plugged in?');
|
||||
}
|
||||
|
||||
deferred.reject('Unable to mount developer disk image.');
|
||||
} else {
|
||||
deferred.resolve({});
|
||||
}
|
||||
});
|
||||
imagemounter.on('error', function(err: any): void {
|
||||
deferred.reject(err);
|
||||
});
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
private static getDiskImage(): Q.Promise<string> {
|
||||
// Attempt to find the OS version of the iDevice, e.g. 7.1
|
||||
let versionInfo: Q.Promise<any> = promiseExec('ideviceinfo -s -k ProductVersion').spread<string>(function (stdout: string, stderr: string): string {
|
||||
return stdout.trim().substring(0, 3); // Versions for DeveloperDiskImage seem to be X.Y, while some device versions are X.Y.Z
|
||||
// NOTE: This will almost certainly be wrong in the next few years, once we hit version 10.0
|
||||
}, function (): string {
|
||||
throw new Error('Unable to get device OS version');
|
||||
});
|
||||
|
||||
// Attempt to find the path where developer resources exist.
|
||||
let pathInfo: Q.Promise<any> = promiseExec('xcrun -sdk iphoneos --show-sdk-platform-path').spread<string>(function (stdout: string, stderr: string): string {
|
||||
let sdkpath: string = stdout.trim();
|
||||
return sdkpath;
|
||||
});
|
||||
|
||||
// Attempt to find the developer disk image for the appropriate
|
||||
return Q.all([versionInfo, pathInfo]).spread<string>(function (version: string, sdkpath: string): Q.Promise<string> {
|
||||
let find: child_process.ChildProcess = child_process.spawn('find', [sdkpath, '-path', '*' + version + '*', '-name', 'DeveloperDiskImage.dmg']);
|
||||
let deferred: Q.Deferred<string> = Q.defer<string>();
|
||||
|
||||
find.stdout.on('data', function (data: any): void {
|
||||
let dataStr: string = data.toString();
|
||||
let path: string = dataStr.split('\n')[0].trim();
|
||||
if (!path) {
|
||||
deferred.reject('Unable to find developer disk image');
|
||||
} else {
|
||||
deferred.resolve(path);
|
||||
}
|
||||
});
|
||||
find.on('close', function (code: number): void {
|
||||
deferred.reject('Unable to find developer disk image');
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
private static makeGdbCommand(command: string): string {
|
||||
let commandString: string = '$' + command + '#';
|
||||
let stringSum: number = 0;
|
||||
for (let i: number = 0; i < command.length; i++) {
|
||||
stringSum += command.charCodeAt(i);
|
||||
}
|
||||
|
||||
/* tslint:disable:no-bitwise */
|
||||
// We need some bitwise operations to calculate the checksum
|
||||
stringSum = stringSum & 0xFF;
|
||||
/* tslint:enable:no-bitwise */
|
||||
let checksum: string = stringSum.toString(16).toUpperCase();
|
||||
if (checksum.length < 2) {
|
||||
checksum = '0' + checksum;
|
||||
}
|
||||
|
||||
commandString += checksum;
|
||||
return commandString;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as utils from '../webkit/utilities';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
interface IPendingBreakpoint {
|
||||
resolve: () => void;
|
||||
reject: (e: Error) => void;
|
||||
args: ISetBreakpointsArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a local path from Code to a path on the target.
|
||||
*/
|
||||
export class CordovaPathTransformer implements IDebugTransformer {
|
||||
private _cordovaRoot: string;
|
||||
private _platform: string;
|
||||
private _clientPathToWebkitUrl = new Map<string, string>();
|
||||
private _webkitUrlToClientPath = new Map<string, string>();
|
||||
private _shadowedClientPaths = new Map<string, string>();
|
||||
private _pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
|
||||
private _outputLogger: (message: string, error?: boolean) => void;
|
||||
|
||||
constructor(outputLogger: (message: string) => void) {
|
||||
this._outputLogger = outputLogger;
|
||||
}
|
||||
|
||||
public launch(args: ICordovaLaunchRequestArgs): void {
|
||||
this.attach(args);
|
||||
}
|
||||
|
||||
public attach(args: ICordovaAttachRequestArgs): void {
|
||||
this._cordovaRoot = args.cwd;
|
||||
this._platform = args.platform.toLowerCase();
|
||||
}
|
||||
|
||||
public setBreakpoints(args: ISetBreakpointsArgs): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
|
||||
if (!args.source.path) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isURL(args.source.path)) {
|
||||
// already a url, use as-is
|
||||
utils.Logger.log(`Paths.setBP: ${args.source.path} is already a URL`);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const url = utils.canonicalizeUrl(args.source.path);
|
||||
if (this._clientPathToWebkitUrl.has(url)) {
|
||||
args.source.path = this._clientPathToWebkitUrl.get(url);
|
||||
utils.Logger.log(`Paths.setBP: Resolved ${url} to ${args.source.path}`);
|
||||
resolve();
|
||||
} else if (this._shadowedClientPaths.has(url)) {
|
||||
this._outputLogger(`Warning: Breakpoint set in overriden file ${url} will not be hit. Use ${this._shadowedClientPaths.get(url)} instead.`, true);
|
||||
reject();
|
||||
} else {
|
||||
utils.Logger.log(`Paths.setBP: No target url cached for client path: ${url}, waiting for target script to be loaded.`);
|
||||
args.source.path = url;
|
||||
this._pendingBreakpointsByPath.set(args.source.path, { resolve, reject, args });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public clearClientContext(): void {
|
||||
this._pendingBreakpointsByPath = new Map<string, IPendingBreakpoint>();
|
||||
}
|
||||
|
||||
public clearTargetContext(): void {
|
||||
this._clientPathToWebkitUrl = new Map<string, string>();
|
||||
this._webkitUrlToClientPath = new Map<string, string>();
|
||||
this._shadowedClientPaths = new Map<string, string>();
|
||||
}
|
||||
|
||||
public scriptParsed(event: DebugProtocol.Event): void {
|
||||
const webkitUrl: string = event.body.scriptUrl;
|
||||
const clientPath = this.getClientPath(webkitUrl);
|
||||
|
||||
if (!clientPath) {
|
||||
utils.Logger.log(`Paths.scriptParsed: could not resolve ${webkitUrl} to a file in the workspace. webRoot: ${this._cordovaRoot}`);
|
||||
} else {
|
||||
utils.Logger.log(`Paths.scriptParsed: resolved ${webkitUrl} to ${clientPath}. webRoot: ${this._cordovaRoot}`);
|
||||
this._clientPathToWebkitUrl.set(clientPath, webkitUrl);
|
||||
this._webkitUrlToClientPath.set(webkitUrl, clientPath);
|
||||
|
||||
event.body.scriptUrl = clientPath;
|
||||
}
|
||||
|
||||
if (this._pendingBreakpointsByPath.has(event.body.scriptUrl)) {
|
||||
utils.Logger.log(`Paths.scriptParsed: Resolving pending breakpoints for ${event.body.scriptUrl}`);
|
||||
const pendingBreakpoint = this._pendingBreakpointsByPath.get(event.body.scriptUrl);
|
||||
this._pendingBreakpointsByPath.delete(event.body.scriptUrl);
|
||||
this.setBreakpoints(pendingBreakpoint.args).then(pendingBreakpoint.resolve, pendingBreakpoint.reject);
|
||||
}
|
||||
}
|
||||
|
||||
public stackTraceResponse(response: IStackTraceResponseBody): void {
|
||||
response.stackFrames.forEach(frame => {
|
||||
if (frame.source.path) {
|
||||
// Try to resolve the url to a path in the workspace. If it's not in the workspace,
|
||||
// just use the script.url as-is. It will be resolved or cleared by the SourceMapTransformer.
|
||||
const clientPath = this._webkitUrlToClientPath.has(frame.source.path) ?
|
||||
this._webkitUrlToClientPath.get(frame.source.path) :
|
||||
this.getClientPath(frame.source.path);
|
||||
|
||||
// Incoming stackFrames have sourceReference and path set. If the path was resolved to a file in the workspace,
|
||||
// clear the sourceReference since it's not needed.
|
||||
if (clientPath) {
|
||||
frame.source.path = clientPath;
|
||||
frame.source.sourceReference = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getClientPath(sourceUrl: string): string {
|
||||
let defaultPath = utils.webkitUrlToClientPath(this._cordovaRoot, sourceUrl);
|
||||
let wwwRoot = path.join(this._cordovaRoot, 'www');
|
||||
if (defaultPath.toLowerCase().indexOf(wwwRoot.toLowerCase()) === 0) {
|
||||
// If the path appears to be in www, check to see if it exists in /merges/<platform>/<relative path>
|
||||
let relativePath = path.relative(wwwRoot, defaultPath);
|
||||
let mergesPath = path.join(this._cordovaRoot, 'merges', this._platform, relativePath);
|
||||
if (fs.existsSync(mergesPath)) {
|
||||
// This file is overriden by a merge: Use that one
|
||||
if (fs.existsSync(defaultPath)) {
|
||||
this._shadowedClientPaths.set(defaultPath, mergesPath);
|
||||
if (this._pendingBreakpointsByPath.has(defaultPath)) {
|
||||
this._outputLogger(`Warning: Breakpoint set in overriden file ${defaultPath} will not be hit. Use ${mergesPath} instead.`, true);
|
||||
}
|
||||
}
|
||||
return mergesPath;
|
||||
}
|
||||
}
|
||||
return defaultPath;
|
||||
}
|
||||
}
|
||||
;
|
|
@ -0,0 +1,8 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import {CordovaDebugSession} from './cordovaDebugSession';
|
||||
import {DebugSession} from '../common/debugSession';
|
||||
|
||||
DebugSession.run(CordovaDebugSession);
|
|
@ -0,0 +1,50 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import * as os from 'os';
|
||||
import * as Q from 'q';
|
||||
|
||||
export function execCommand(command: string, errorLogger: (message: string) => void, options: any = {}): Q.Promise<string> {
|
||||
let deferred = Q.defer<string>();
|
||||
child_process.exec(command, options, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
errorLogger(stderr.toString());
|
||||
errorLogger(stdout.toString());
|
||||
deferred.reject(error);
|
||||
} else {
|
||||
deferred.resolve(stdout.toString());
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
export function cordovaRunCommand(args: string[], errorLogger: (message: string) => void, cordovaRootPath: string): Q.Promise<string[]> {
|
||||
let defer = Q.defer<string[]>();
|
||||
|
||||
let output = '';
|
||||
let stderr = '';
|
||||
let cordovaCommand = `cordova${os.platform() === 'win32' ? '.cmd' : ''}`;
|
||||
let process = child_process.spawn(cordovaCommand, args, {cwd: cordovaRootPath});
|
||||
process.stderr.on('data', data => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
process.stdout.on('data', data => {
|
||||
output += data.toString();
|
||||
});
|
||||
process.on('exit', exitCode => {
|
||||
if (exitCode) {
|
||||
errorLogger(stderr);
|
||||
errorLogger(output);
|
||||
defer.reject(new Error(`'cordova ${args.join(' ')}' failed with exit code ${exitCode}.`));
|
||||
} else {
|
||||
defer.resolve([output, stderr]);
|
||||
}
|
||||
});
|
||||
process.on('error', error => {
|
||||
defer.reject(error);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
Двоичные данные
debugger/images/screenshot.png
Двоичные данные
debugger/images/screenshot.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 678 KiB |
|
@ -1,167 +0,0 @@
|
|||
{
|
||||
"name": "debugger-for-chrome",
|
||||
"displayName": "Debugger for Chrome",
|
||||
"version": "0.2.1",
|
||||
"icon": "images/icon.png",
|
||||
"description": "Debug your JavaScript code in the Chrome browser, or any other target that supports the Chrome debug protocol.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/vscode-chrome-debug"
|
||||
},
|
||||
"publisher": "msjsdiag",
|
||||
"bugs": "https://github.com/microsoft/vscode-chrome-debug/issues",
|
||||
"engines": {
|
||||
"vscode": "*"
|
||||
},
|
||||
"categories": ["Debuggers"],
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"dependencies": {
|
||||
"source-map": "^0.5.3",
|
||||
"ws": "0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-mocha": "^2.1.3",
|
||||
"gulp-sourcemaps": "^1.5.2",
|
||||
"gulp-tslint": "^3.3.1",
|
||||
"gulp-typescript": "^2.8.0",
|
||||
"gulp-util": "^3.0.5",
|
||||
"mocha": "^2.3.3",
|
||||
"mockery": "^1.4.0",
|
||||
"sinon": "^1.17.2",
|
||||
"tsd": "^0.6.3",
|
||||
"tslint": "^2.5.1",
|
||||
"typescript": "^1.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node ./node_modules/mocha/bin/mocha --recursive -u tdd ./out/test/"
|
||||
},
|
||||
"contributes": {
|
||||
"debuggers": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"label": "Chrome",
|
||||
"enableBreakpointsFor": {
|
||||
"languageIds": [
|
||||
"javascript",
|
||||
"typescriptreact"
|
||||
]
|
||||
},
|
||||
"program": "./out/webkit/webkitDebug.js",
|
||||
"runtime": "node",
|
||||
"initialConfigurations": [
|
||||
{
|
||||
"name": "Launch index.html",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"file": "${workspaceRoot}/index.html"
|
||||
},
|
||||
{
|
||||
"name": "Launch localhost with sourcemaps",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost/mypage.html",
|
||||
"sourceMaps": true,
|
||||
"webRoot": "wwwroot"
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "chrome",
|
||||
"request": "attach",
|
||||
"port": 9222
|
||||
}
|
||||
],
|
||||
"configurationAttributes": {
|
||||
"launch": {
|
||||
"required": [ ],
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "A local html file to open in the browser",
|
||||
"default": "index.html"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "A url to open in the browser",
|
||||
"default": "http://mysite.com/index.html"
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "DEPRECATED - renamed to webRoot",
|
||||
"default": "."
|
||||
},
|
||||
"webRoot": {
|
||||
"type": "string",
|
||||
"description": "When the 'url' field is specified, this specifies the workspace relative or absolute path to the webserver root.",
|
||||
"default": "."
|
||||
},
|
||||
"runtimeExecutable": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Workspace relative or absolute path to the runtime executable to be used. If not specified, Chrome will be used from the default install location.",
|
||||
"default": null
|
||||
},
|
||||
"runtimeArgs": {
|
||||
"type": "array",
|
||||
"description": "Optional arguments passed to the runtime executable.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Use JavaScript source maps (if they exist).",
|
||||
"default": true
|
||||
},
|
||||
"diagnosticLogging": {
|
||||
"type": "boolean",
|
||||
"description": "When true, the adapter logs its own diagnostic info to the console",
|
||||
"default": false
|
||||
},
|
||||
"userDataDir": {
|
||||
"type": "string",
|
||||
"description": "When set, Chrome is launched with the --user-data-dir option set to this path",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"attach": {
|
||||
"required": [
|
||||
"port"
|
||||
],
|
||||
"properties": {
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to use for Chrome remote debugging.",
|
||||
"default": 9222
|
||||
},
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Use JavaScript source maps (if they exist).",
|
||||
"default": true
|
||||
},
|
||||
"diagnosticLogging": {
|
||||
"type": "boolean",
|
||||
"description": "When true, the adapter logs its own diagnostic info to the console",
|
||||
"default": false
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "DEPRECATED - renamed to webRoot",
|
||||
"default": "."
|
||||
},
|
||||
"webRoot": {
|
||||
"type": "string",
|
||||
"description": "When the 'url' field is specified, this specifies the workspace relative or absolute path to the webserver root.",
|
||||
"default": "."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -318,6 +318,6 @@ class StubSourceMaps implements ISourceMaps {
|
|||
}
|
||||
|
||||
public ProcessNewSourceMap(pathToGenerated: string, sourceMapURL: string): Promise<void> {
|
||||
return Promise.resolve<void>();
|
||||
return Promise.resolve<void>(void 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import * as mockery from 'mockery';
|
||||
|
||||
// Used only for the type to allow mocking
|
||||
import {CordovaIosDeviceLauncher as _CordovaIosDeviceLauncher} from '../../cordova/cordovaIosDeviceLauncher';
|
||||
|
||||
let CordovaIosDeviceLauncher: typeof _CordovaIosDeviceLauncher;
|
||||
|
||||
describe('cordovaIosDeviceLauncher', function () {
|
||||
let plistMock: any = {};
|
||||
let fsMock: any = {};
|
||||
|
||||
before(() => {
|
||||
mockery.enable({warnOnReplace: false, useCleanCache: true});
|
||||
mockery.registerAllowables([
|
||||
'../../cordova/cordovaIosDeviceLauncher',
|
||||
'path',
|
||||
'q'
|
||||
]);
|
||||
mockery.registerMock('child_process', {});
|
||||
mockery.registerMock('net', {});
|
||||
|
||||
mockery.registerMock('fs', fsMock);
|
||||
mockery.registerMock('plist-with-patches', plistMock);
|
||||
CordovaIosDeviceLauncher = require('../../cordova/cordovaIosDeviceLauncher').CordovaIosDeviceLauncher;
|
||||
});
|
||||
after(() => {
|
||||
mockery.disable();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let mocksToReset = [fsMock, plistMock];
|
||||
mocksToReset.forEach(mock => {
|
||||
for (let prop in mock) {
|
||||
if (mock.hasOwnProperty(prop)) {
|
||||
delete mock[prop];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to find the bundle identifier', () => {
|
||||
fsMock.readdir = (path: string, callback: (err: Error, result: string[]) => void) => callback(null, ['foo', 'bar.xcodeproj']);
|
||||
plistMock.parseFileSync = (file: string) => {
|
||||
return {CFBundleIdentifier: 'test.bundle.identifier'};
|
||||
};
|
||||
|
||||
return CordovaIosDeviceLauncher.getBundleIdentifier('testApp').then((bundleId) => {
|
||||
bundleId.should.equal('test.bundle.identifier');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import {CordovaPathTransformer} from '../../cordova/cordovaPathTransformer';
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import 'should';
|
||||
|
||||
describe('CordovaPathTransformer', () => {
|
||||
|
||||
it('should correctly convert merges paths for android', () => {
|
||||
let output = '';
|
||||
let logger = (message: string) => output += message + '\n';
|
||||
let pathTransformer = new CordovaPathTransformer(logger);
|
||||
|
||||
// __dirname is '/out/debugger/test/cordova' so we need to step up four levels to escape completely
|
||||
let testapp = path.join(__dirname, '..', '..', '..', '..', 'debugger', 'testCordovaApp');
|
||||
pathTransformer.attach({cwd: testapp, platform: 'android', port: 1234});
|
||||
|
||||
pathTransformer.getClientPath('file:///android_asset/www/js/index.js').toLowerCase().should.equal(path.resolve(testapp, 'www', 'js', 'index.js').toLowerCase());
|
||||
pathTransformer.getClientPath('file:///android_asset/www/js/merged.js').toLowerCase().should.equal(path.resolve(testapp, 'merges', 'android', 'js', 'merged.js').toLowerCase());
|
||||
});
|
||||
});
|
|
@ -300,7 +300,7 @@ class DefaultMockWebKitConnection {
|
|||
}
|
||||
|
||||
public attach(port: number): Promise<void> {
|
||||
return Promise.resolve<void>();
|
||||
return Promise.resolve<void>(void 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
//"debugServer": "4712",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Android on device",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "android",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run iOS on device",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "ios",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running android on device",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "android",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running iOS on device",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "ios",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run Android on emulator",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "android",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run iOS on emulator",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "ios",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running android on emulator",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "android",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running iOS on emulator",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "ios",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>HelloCordova</name>
|
||||
<description>
|
||||
A sample Apache Cordova application that responds to the deviceready event.
|
||||
</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<plugin name="cordova-plugin-whitelist" version="1" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<plugin name="cordova-plugin-file" spec="~3.0.0" />
|
||||
</widget>
|
|
@ -0,0 +1,196 @@
|
|||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-->
|
||||
# Cordova Hooks
|
||||
|
||||
Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. Hook scripts could be defined by adding them to the special predefined folder (`/hooks`) or via configuration files (`config.xml` and `plugin.xml`) and run serially in the following order:
|
||||
* Application hooks from `/hooks`;
|
||||
* Application hooks from `config.xml`;
|
||||
* Plugin hooks from `plugins/.../plugin.xml`.
|
||||
|
||||
__Remember__: Make your scripts executable.
|
||||
|
||||
__Note__: `.cordova/hooks` directory is also supported for backward compatibility, but we don't recommend using it as it is deprecated.
|
||||
|
||||
## Supported hook types
|
||||
The following hook types are supported:
|
||||
|
||||
after_build/
|
||||
after_compile/
|
||||
after_docs/
|
||||
after_emulate/
|
||||
after_platform_add/
|
||||
after_platform_rm/
|
||||
after_platform_ls/
|
||||
after_plugin_add/
|
||||
after_plugin_ls/
|
||||
after_plugin_rm/
|
||||
after_plugin_search/
|
||||
after_plugin_install/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed
|
||||
after_prepare/
|
||||
after_run/
|
||||
after_serve/
|
||||
before_build/
|
||||
before_compile/
|
||||
before_docs/
|
||||
before_emulate/
|
||||
before_platform_add/
|
||||
before_platform_rm/
|
||||
before_platform_ls/
|
||||
before_plugin_add/
|
||||
before_plugin_ls/
|
||||
before_plugin_rm/
|
||||
before_plugin_search/
|
||||
before_plugin_install/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being installed
|
||||
before_plugin_uninstall/ <-- Plugin hooks defined in plugin.xml are executed exclusively for a plugin being uninstalled
|
||||
before_prepare/
|
||||
before_run/
|
||||
before_serve/
|
||||
pre_package/ <-- Windows 8 and Windows Phone only.
|
||||
|
||||
## Ways to define hooks
|
||||
### Via '/hooks' directory
|
||||
To execute custom action when corresponding hook type is fired, use hook type as a name for a subfolder inside 'hooks' directory and place you script file here, for example:
|
||||
|
||||
# script file will be automatically executed after each build
|
||||
hooks/after_build/after_build_custom_action.js
|
||||
|
||||
|
||||
### Config.xml
|
||||
|
||||
Hooks can be defined in project's `config.xml` using `<hook>` elements, for example:
|
||||
|
||||
<hook type="before_build" src="scripts/appBeforeBuild.bat" />
|
||||
<hook type="before_build" src="scripts/appBeforeBuild.js" />
|
||||
<hook type="before_plugin_install" src="scripts/appBeforePluginInstall.js" />
|
||||
|
||||
<platform name="wp8">
|
||||
<hook type="before_build" src="scripts/wp8/appWP8BeforeBuild.bat" />
|
||||
<hook type="before_build" src="scripts/wp8/appWP8BeforeBuild.js" />
|
||||
<hook type="before_plugin_install" src="scripts/wp8/appWP8BeforePluginInstall.js" />
|
||||
...
|
||||
</platform>
|
||||
|
||||
<platform name="windows8">
|
||||
<hook type="before_build" src="scripts/windows8/appWin8BeforeBuild.bat" />
|
||||
<hook type="before_build" src="scripts/windows8/appWin8BeforeBuild.js" />
|
||||
<hook type="before_plugin_install" src="scripts/windows8/appWin8BeforePluginInstall.js" />
|
||||
...
|
||||
</platform>
|
||||
|
||||
### Plugin hooks (plugin.xml)
|
||||
|
||||
As a plugin developer you can define hook scripts using `<hook>` elements in a `plugin.xml` like that:
|
||||
|
||||
<hook type="before_plugin_install" src="scripts/beforeInstall.js" />
|
||||
<hook type="after_build" src="scripts/afterBuild.js" />
|
||||
|
||||
<platform name="wp8">
|
||||
<hook type="before_plugin_install" src="scripts/wp8BeforeInstall.js" />
|
||||
<hook type="before_build" src="scripts/wp8BeforeBuild.js" />
|
||||
...
|
||||
</platform>
|
||||
|
||||
`before_plugin_install`, `after_plugin_install`, `before_plugin_uninstall` plugin hooks will be fired exclusively for the plugin being installed/uninstalled.
|
||||
|
||||
## Script Interface
|
||||
|
||||
### Javascript
|
||||
|
||||
If you are writing hooks in Javascript you should use the following module definition:
|
||||
```javascript
|
||||
module.exports = function(context) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
You can make your scipts async using Q:
|
||||
```javascript
|
||||
module.exports = function(context) {
|
||||
var Q = context.requireCordovaModule('q');
|
||||
var deferral = new Q.defer();
|
||||
|
||||
setTimeout(function(){
|
||||
console.log('hook.js>> end');
|
||||
deferral.resolve();
|
||||
}, 1000);
|
||||
|
||||
return deferral.promise;
|
||||
}
|
||||
```
|
||||
|
||||
`context` object contains hook type, executed script full path, hook options, command-line arguments passed to Cordova and top-level "cordova" object:
|
||||
```json
|
||||
{
|
||||
"hook": "before_plugin_install",
|
||||
"scriptLocation": "c:\\script\\full\\path\\appBeforePluginInstall.js",
|
||||
"cmdLine": "The\\exact\\command\\cordova\\run\\with arguments",
|
||||
"opts": {
|
||||
"projectRoot":"C:\\path\\to\\the\\project",
|
||||
"cordova": {
|
||||
"platforms": ["wp8"],
|
||||
"plugins": ["com.plugin.withhooks"],
|
||||
"version": "0.21.7-dev"
|
||||
},
|
||||
"plugin": {
|
||||
"id": "com.plugin.withhooks",
|
||||
"pluginInfo": {
|
||||
...
|
||||
},
|
||||
"platform": "wp8",
|
||||
"dir": "C:\\path\\to\\the\\project\\plugins\\com.plugin.withhooks"
|
||||
}
|
||||
},
|
||||
"cordova": {...}
|
||||
}
|
||||
|
||||
```
|
||||
`context.opts.plugin` object will only be passed to plugin hooks scripts.
|
||||
|
||||
You can also require additional Cordova modules in your script using `context.requireCordovaModule` in the following way:
|
||||
```javascript
|
||||
var Q = context.requireCordovaModule('q');
|
||||
```
|
||||
|
||||
__Note__: new module loader script interface is used for the `.js` files defined via `config.xml` or `plugin.xml` only.
|
||||
For compatibility reasons hook files specified via `/hooks` folders are run via Node child_process spawn, see 'Non-javascript' section below.
|
||||
|
||||
### Non-javascript
|
||||
|
||||
Non-javascript scripts are run via Node child_process spawn from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables:
|
||||
|
||||
* CORDOVA_VERSION - The version of the Cordova-CLI.
|
||||
* CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios).
|
||||
* CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer)
|
||||
* CORDOVA_HOOK - Path to the hook that is being executed.
|
||||
* CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate)
|
||||
|
||||
If a script returns a non-zero exit code, then the parent cordova command will be aborted.
|
||||
|
||||
## Writing hooks
|
||||
|
||||
We highly recommend writing your hooks using Node.js so that they are
|
||||
cross-platform. Some good examples are shown here:
|
||||
|
||||
[http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/)
|
||||
|
||||
Also, note that even if you are working on Windows, and in case your hook scripts aren't bat files (which is recommended, if you want your scripts to work in non-Windows operating systems) Cordova CLI will expect a shebang line as the first line for it to know the interpreter it needs to use to launch the script. The shebang line should match the following example:
|
||||
|
||||
#!/usr/bin/env [name_of_interpreter_executable]
|
|
@ -0,0 +1,3 @@
|
|||
function mergedScript() {
|
||||
console.info("Debugging android merge file");
|
||||
};
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
* {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
|
||||
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
|
||||
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
|
||||
background-color:#E4E4E4;
|
||||
background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0, #A7A7A7),
|
||||
color-stop(0.51, #E4E4E4)
|
||||
);
|
||||
background-attachment:fixed;
|
||||
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
font-size:12px;
|
||||
height:100%;
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
text-transform:uppercase;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
/* Portrait layout (default) */
|
||||
.app {
|
||||
background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
|
||||
position:absolute; /* position in the center of the screen */
|
||||
left:50%;
|
||||
top:50%;
|
||||
height:50px; /* text area height */
|
||||
width:225px; /* text area width */
|
||||
text-align:center;
|
||||
padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */
|
||||
margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */
|
||||
/* offset horizontal: half of text area width */
|
||||
}
|
||||
|
||||
/* Landscape layout (with min-width) */
|
||||
@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
|
||||
.app {
|
||||
background-position:left center;
|
||||
padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */
|
||||
margin:-90px 0px 0px -198px; /* offset vertical: half of image height */
|
||||
/* offset horizontal: half of image width and text area width */
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:24px;
|
||||
font-weight:normal;
|
||||
margin:0px;
|
||||
overflow:visible;
|
||||
padding:0px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.event {
|
||||
border-radius:4px;
|
||||
-webkit-border-radius:4px;
|
||||
color:#FFFFFF;
|
||||
font-size:12px;
|
||||
margin:0px 30px;
|
||||
padding:2px 0px;
|
||||
}
|
||||
|
||||
.event.listening {
|
||||
background-color:#333333;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.event.received {
|
||||
background-color:#4B946A;
|
||||
display:none;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation:fade 3000ms infinite;
|
||||
-webkit-animation:fade 3000ms infinite;
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 21 KiB |
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
Customize this policy to fit your own app's needs. For more guidance, see:
|
||||
https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
|
||||
Some notes:
|
||||
* gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
|
||||
* https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
|
||||
* Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
|
||||
* Enable inline JS: add 'unsafe-inline' to default-src
|
||||
-->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css">
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="cordova.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript" src="js/merged.js"></script>
|
||||
<script type="text/javascript" src="js/typescript.js"></script>
|
||||
|
||||
<script>
|
||||
function embeddedScript() {
|
||||
console.log("Running embedded script");
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick="embeddedScript()">Embedded script test</button>
|
||||
<button onclick="app.indexjsScript()">index.js script test</button>
|
||||
<button onclick="mergedScript()">merges script test</button>
|
||||
<button onclick="app.pluginScript()">plugin script test</button>
|
||||
<button onclick="app.causeException()">exception test</button>
|
||||
<button onclick="typeScript()">Typescript test</button>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function() {
|
||||
this.bindEvents();
|
||||
},
|
||||
// Bind Event Listeners
|
||||
//
|
||||
// Bind any events that are required on startup. Common events are:
|
||||
// 'load', 'deviceready', 'offline', and 'online'.
|
||||
bindEvents: function() {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
// deviceready Event Handler
|
||||
//
|
||||
// The scope of 'this' is the event. In order to call the 'receivedEvent'
|
||||
// function, we must explicitly call 'app.receivedEvent(...);'
|
||||
onDeviceReady: function() {
|
||||
app.receivedEvent('deviceready');
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedEvent: function(id) {
|
||||
console.log('Received Event: ' + id);
|
||||
},
|
||||
|
||||
indexjsScript: function () {
|
||||
console.log("Running script in index.js");
|
||||
},
|
||||
|
||||
pluginScript: function () {
|
||||
resolveLocalFileSystemURL('cdvfile://localhost/temporary/foo', function (entry) {
|
||||
console.info(entry);
|
||||
})
|
||||
},
|
||||
|
||||
causeException: function () {
|
||||
undefinedFunction();
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -0,0 +1,3 @@
|
|||
function mergedScript() {
|
||||
console.info("Debugging base merge file");
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
var testClass = (function () {
|
||||
function testClass(val) {
|
||||
this.value = val;
|
||||
}
|
||||
testClass.prototype.printValue = function () {
|
||||
console.info("Debugging typescript: " + this.value);
|
||||
};
|
||||
return testClass;
|
||||
})();
|
||||
function typeScript() {
|
||||
var obj = new testClass(42);
|
||||
obj.printValue();
|
||||
}
|
||||
//# sourceMappingURL=typescript.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"typescript.js","sourceRoot":"","sources":["typescript.ts"],"names":["testClass","testClass.constructor","testClass.printValue","typeScript"],"mappings":"AAAA;IAEIA,mBAAYA,GAAWA;QACnBC,IAAIA,CAACA,KAAKA,GAAGA,GAAGA,CAACA;IACrBA,CAACA;IAEMD,8BAAUA,GAAjBA;QACIE,OAAOA,CAACA,IAAIA,CAACA,wBAAwBA,GAAGA,IAAIA,CAACA,KAAKA,CAACA,CAACA;IACxDA,CAACA;IACLF,gBAACA;AAADA,CAACA,AATD,IASC;AAED;IACIG,IAAIA,GAAGA,GAAGA,IAAIA,SAASA,CAACA,EAAEA,CAACA,CAACA;IAC5BA,GAAGA,CAACA,UAAUA,EAAEA,CAACA;AACrBA,CAACA"}
|
|
@ -0,0 +1,15 @@
|
|||
class testClass {
|
||||
private value: number;
|
||||
constructor(val: number) {
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
public printValue(): void {
|
||||
console.info("Debugging typescript: " + this.value);
|
||||
}
|
||||
}
|
||||
|
||||
function typeScript(): void {
|
||||
let obj = new testClass(42);
|
||||
obj.printValue();
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"version": "v4",
|
||||
"repo": "borisyankov/DefinitelyTyped",
|
||||
"ref": "master",
|
||||
"path": "typings",
|
||||
"bundle": "typings/tsd.d.ts",
|
||||
"installed": {
|
||||
"node/node.d.ts": {
|
||||
"commit": "4ba73ef59f93e38a70573cfc46375de37b004635"
|
||||
},
|
||||
"ws/ws.d.ts": {
|
||||
"commit": "b45569f35d5f27716d21cc2a639c7d8ccb9a0164"
|
||||
},
|
||||
"es6-promise/es6-promise.d.ts": {
|
||||
"commit": "5109e1269d6ab8e8e73b1a5e85d8ceb836d3099f"
|
||||
},
|
||||
"es6-collections/es6-collections.d.ts": {
|
||||
"commit": "5109e1269d6ab8e8e73b1a5e85d8ceb836d3099f"
|
||||
},
|
||||
"source-map/source-map.d.ts": {
|
||||
"commit": "bb45306d0fd8ce0852a8d24c023fd787f4151e65"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
// Type definitions for es6-collections v0.5.1
|
||||
// Project: https://github.com/WebReflection/es6-collections/
|
||||
// Definitions by: Ron Buckton <http://github.com/rbuckton>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/* *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
interface IteratorResult<T> {
|
||||
done: boolean;
|
||||
value?: T;
|
||||
}
|
||||
|
||||
interface Iterator<T> {
|
||||
next(value?: any): IteratorResult<T>;
|
||||
return?(value?: any): IteratorResult<T>;
|
||||
throw?(e?: any): IteratorResult<T>;
|
||||
}
|
||||
|
||||
interface ForEachable<T> {
|
||||
forEach(callbackfn: (value: T) => void): void;
|
||||
}
|
||||
|
||||
interface Map<K, V> {
|
||||
clear(): void;
|
||||
delete(key: K): boolean;
|
||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||
get(key: K): V;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): Map<K, V>;
|
||||
entries(): Iterator<[K, V]>;
|
||||
keys(): Iterator<K>;
|
||||
values(): Iterator<V>;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface MapConstructor {
|
||||
new <K, V>(): Map<K, V>;
|
||||
new <K, V>(iterable: ForEachable<[K, V]>): Map<K, V>;
|
||||
prototype: Map<any, any>;
|
||||
}
|
||||
|
||||
declare var Map: MapConstructor;
|
||||
|
||||
interface Set<T> {
|
||||
add(value: T): Set<T>;
|
||||
clear(): void;
|
||||
delete(value: T): boolean;
|
||||
forEach(callbackfn: (value: T, index: T, set: Set<T>) => void, thisArg?: any): void;
|
||||
has(value: T): boolean;
|
||||
entries(): Iterator<[T, T]>;
|
||||
keys(): Iterator<T>;
|
||||
values(): Iterator<T>;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface SetConstructor {
|
||||
new <T>(): Set<T>;
|
||||
new <T>(iterable: ForEachable<T>): Set<T>;
|
||||
prototype: Set<any>;
|
||||
}
|
||||
|
||||
declare var Set: SetConstructor;
|
||||
|
||||
interface WeakMap<K, V> {
|
||||
delete(key: K): boolean;
|
||||
clear(): void;
|
||||
get(key: K): V;
|
||||
has(key: K): boolean;
|
||||
set(key: K, value?: V): WeakMap<K, V>;
|
||||
}
|
||||
|
||||
interface WeakMapConstructor {
|
||||
new <K, V>(): WeakMap<K, V>;
|
||||
new <K, V>(iterable: ForEachable<[K, V]>): WeakMap<K, V>;
|
||||
prototype: WeakMap<any, any>;
|
||||
}
|
||||
|
||||
declare var WeakMap: WeakMapConstructor;
|
||||
|
||||
interface WeakSet<T> {
|
||||
delete(value: T): boolean;
|
||||
clear(): void;
|
||||
add(value: T): WeakSet<T>;
|
||||
has(value: T): boolean;
|
||||
}
|
||||
|
||||
interface WeakSetConstructor {
|
||||
new <T>(): WeakSet<T>;
|
||||
new <T>(iterable: ForEachable<T>): WeakSet<T>;
|
||||
prototype: WeakSet<any>;
|
||||
}
|
||||
|
||||
declare var WeakSet: WeakSetConstructor;
|
||||
|
||||
declare module "es6-collections" {
|
||||
var Map: MapConstructor;
|
||||
var Set: SetConstructor;
|
||||
var WeakMap: WeakMapConstructor;
|
||||
var WeakSet: WeakSetConstructor;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Type definitions for es6-promise
|
||||
// Project: https://github.com/jakearchibald/ES6-Promise
|
||||
// Definitions by: François de Campredon <https://github.com/fdecampredon/>, vvakame <https://github.com/vvakame>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
interface Thenable<R> {
|
||||
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
|
||||
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
|
||||
}
|
||||
|
||||
declare class Promise<R> implements Thenable<R> {
|
||||
/**
|
||||
* If you call resolve in the body of the callback passed to the constructor,
|
||||
* your promise is fulfilled with result object passed to resolve.
|
||||
* If you call reject your promise is rejected with the object passed to resolve.
|
||||
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
|
||||
* Any errors thrown in the constructor callback will be implicitly passed to reject().
|
||||
*/
|
||||
constructor(callback: (resolve : (value?: R | Thenable<R>) => void, reject: (error?: any) => void) => void);
|
||||
|
||||
/**
|
||||
* onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
|
||||
* Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
|
||||
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
|
||||
* "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
|
||||
* If an error is thrown in the callback, the returned promise rejects with that error.
|
||||
*
|
||||
* @param onFulfilled called when/if "promise" resolves
|
||||
* @param onRejected called when/if "promise" rejects
|
||||
*/
|
||||
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
|
||||
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Promise<U>;
|
||||
|
||||
/**
|
||||
* Sugar for promise.then(undefined, onRejected)
|
||||
*
|
||||
* @param onRejected called when/if "promise" rejects
|
||||
*/
|
||||
catch<U>(onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
|
||||
}
|
||||
|
||||
declare module Promise {
|
||||
/**
|
||||
* Make a new promise from the thenable.
|
||||
* A thenable is promise-like in as far as it has a "then" method.
|
||||
*/
|
||||
function resolve<R>(value?: R | Thenable<R>): Promise<R>;
|
||||
|
||||
/**
|
||||
* Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
|
||||
*/
|
||||
function reject(error: any): Promise<any>;
|
||||
|
||||
/**
|
||||
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
|
||||
* the array passed to all can be a mixture of promise-like objects and other objects.
|
||||
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
|
||||
*/
|
||||
function all<R>(promises: (R | Thenable<R>)[]): Promise<R[]>;
|
||||
|
||||
/**
|
||||
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
|
||||
*/
|
||||
function race<R>(promises: (R | Thenable<R>)[]): Promise<R>;
|
||||
}
|
||||
|
||||
declare module 'es6-promise' {
|
||||
var foo: typeof Promise; // Temp variable to reference Promise in local context
|
||||
module rsvp {
|
||||
export var Promise: typeof foo;
|
||||
}
|
||||
export = rsvp;
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
// Type definitions for mocha 2.2.5
|
||||
// Project: http://mochajs.org/
|
||||
// Definitions by: Kazi Manzur Rashid <https://github.com/kazimanzurrashid/>, otiai10 <https://github.com/otiai10>, jt000 <https://github.com/jt000>, Vadim Macagon <https://github.com/enlight>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
interface MochaSetupOptions {
|
||||
//milliseconds to wait before considering a test slow
|
||||
slow?: number;
|
||||
|
||||
// timeout in milliseconds
|
||||
timeout?: number;
|
||||
|
||||
// ui name "bdd", "tdd", "exports" etc
|
||||
ui?: string;
|
||||
|
||||
//array of accepted globals
|
||||
globals?: any[];
|
||||
|
||||
// reporter instance (function or string), defaults to `mocha.reporters.Spec`
|
||||
reporter?: any;
|
||||
|
||||
// bail on the first test failure
|
||||
bail?: boolean;
|
||||
|
||||
// ignore global leaks
|
||||
ignoreLeaks?: boolean;
|
||||
|
||||
// grep string or regexp to filter tests with
|
||||
grep?: any;
|
||||
}
|
||||
|
||||
interface MochaDone {
|
||||
(error?: Error): void;
|
||||
}
|
||||
|
||||
declare var mocha: Mocha;
|
||||
declare var describe: Mocha.IContextDefinition;
|
||||
declare var xdescribe: Mocha.IContextDefinition;
|
||||
// alias for `describe`
|
||||
declare var context: Mocha.IContextDefinition;
|
||||
// alias for `describe`
|
||||
declare var suite: Mocha.IContextDefinition;
|
||||
declare var it: Mocha.ITestDefinition;
|
||||
declare var xit: Mocha.ITestDefinition;
|
||||
// alias for `it`
|
||||
declare var test: Mocha.ITestDefinition;
|
||||
|
||||
declare function before(action: () => void): void;
|
||||
|
||||
declare function before(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function setup(action: () => void): void;
|
||||
|
||||
declare function setup(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function after(action: () => void): void;
|
||||
|
||||
declare function after(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function teardown(action: () => void): void;
|
||||
|
||||
declare function teardown(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function beforeEach(action: () => void): void;
|
||||
|
||||
declare function beforeEach(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function suiteSetup(action: () => void): void;
|
||||
|
||||
declare function suiteSetup(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function afterEach(action: () => void): void;
|
||||
|
||||
declare function afterEach(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare function suiteTeardown(action: () => void): void;
|
||||
|
||||
declare function suiteTeardown(action: (done: MochaDone) => void): void;
|
||||
|
||||
declare class Mocha {
|
||||
constructor(options?: {
|
||||
grep?: RegExp;
|
||||
ui?: string;
|
||||
reporter?: string;
|
||||
timeout?: number;
|
||||
bail?: boolean;
|
||||
});
|
||||
|
||||
/** Setup mocha with the given options. */
|
||||
setup(options: MochaSetupOptions): Mocha;
|
||||
bail(value?: boolean): Mocha;
|
||||
addFile(file: string): Mocha;
|
||||
/** Sets reporter by name, defaults to "spec". */
|
||||
reporter(name: string): Mocha;
|
||||
/** Sets reporter constructor, defaults to mocha.reporters.Spec. */
|
||||
reporter(reporter: (runner: Mocha.IRunner, options: any) => any): Mocha;
|
||||
ui(value: string): Mocha;
|
||||
grep(value: string): Mocha;
|
||||
grep(value: RegExp): Mocha;
|
||||
invert(): Mocha;
|
||||
ignoreLeaks(value: boolean): Mocha;
|
||||
checkLeaks(): Mocha;
|
||||
/**
|
||||
* Function to allow assertion libraries to throw errors directly into mocha.
|
||||
* This is useful when running tests in a browser because window.onerror will
|
||||
* only receive the 'message' attribute of the Error.
|
||||
*/
|
||||
throwError(error: Error): void;
|
||||
/** Enables growl support. */
|
||||
growl(): Mocha;
|
||||
globals(value: string): Mocha;
|
||||
globals(values: string[]): Mocha;
|
||||
useColors(value: boolean): Mocha;
|
||||
useInlineDiffs(value: boolean): Mocha;
|
||||
timeout(value: number): Mocha;
|
||||
slow(value: number): Mocha;
|
||||
enableTimeouts(value: boolean): Mocha;
|
||||
asyncOnly(value: boolean): Mocha;
|
||||
noHighlighting(value: boolean): Mocha;
|
||||
/** Runs tests and invokes `onComplete()` when finished. */
|
||||
run(onComplete?: (failures: number) => void): Mocha.IRunner;
|
||||
}
|
||||
|
||||
// merge the Mocha class declaration with a module
|
||||
declare module Mocha {
|
||||
/** Partial interface for Mocha's `Runnable` class. */
|
||||
interface IRunnable {
|
||||
title: string;
|
||||
fn: Function;
|
||||
async: boolean;
|
||||
sync: boolean;
|
||||
timedOut: boolean;
|
||||
}
|
||||
|
||||
/** Partial interface for Mocha's `Suite` class. */
|
||||
interface ISuite {
|
||||
parent: ISuite;
|
||||
title: string;
|
||||
|
||||
fullTitle(): string;
|
||||
}
|
||||
|
||||
/** Partial interface for Mocha's `Test` class. */
|
||||
interface ITest extends IRunnable {
|
||||
parent: ISuite;
|
||||
pending: boolean;
|
||||
|
||||
fullTitle(): string;
|
||||
}
|
||||
|
||||
/** Partial interface for Mocha's `Runner` class. */
|
||||
interface IRunner {}
|
||||
|
||||
interface IContextDefinition {
|
||||
(description: string, spec: () => void): ISuite;
|
||||
only(description: string, spec: () => void): ISuite;
|
||||
skip(description: string, spec: () => void): void;
|
||||
timeout(ms: number): void;
|
||||
}
|
||||
|
||||
interface ITestDefinition {
|
||||
(expectation: string, assertion?: () => void): ITest;
|
||||
(expectation: string, assertion?: (done: MochaDone) => void): ITest;
|
||||
only(expectation: string, assertion?: () => void): ITest;
|
||||
only(expectation: string, assertion?: (done: MochaDone) => void): ITest;
|
||||
skip(expectation: string, assertion?: () => void): void;
|
||||
skip(expectation: string, assertion?: (done: MochaDone) => void): void;
|
||||
timeout(ms: number): void;
|
||||
}
|
||||
|
||||
export module reporters {
|
||||
export class Base {
|
||||
stats: {
|
||||
suites: number;
|
||||
tests: number;
|
||||
passes: number;
|
||||
pending: number;
|
||||
failures: number;
|
||||
};
|
||||
|
||||
constructor(runner: IRunner);
|
||||
}
|
||||
|
||||
export class Doc extends Base {}
|
||||
export class Dot extends Base {}
|
||||
export class HTML extends Base {}
|
||||
export class HTMLCov extends Base {}
|
||||
export class JSON extends Base {}
|
||||
export class JSONCov extends Base {}
|
||||
export class JSONStream extends Base {}
|
||||
export class Landing extends Base {}
|
||||
export class List extends Base {}
|
||||
export class Markdown extends Base {}
|
||||
export class Min extends Base {}
|
||||
export class Nyan extends Base {}
|
||||
export class Progress extends Base {
|
||||
/**
|
||||
* @param options.open String used to indicate the start of the progress bar.
|
||||
* @param options.complete String used to indicate a complete test on the progress bar.
|
||||
* @param options.incomplete String used to indicate an incomplete test on the progress bar.
|
||||
* @param options.close String used to indicate the end of the progress bar.
|
||||
*/
|
||||
constructor(runner: IRunner, options?: {
|
||||
open?: string;
|
||||
complete?: string;
|
||||
incomplete?: string;
|
||||
close?: string;
|
||||
});
|
||||
}
|
||||
export class Spec extends Base {}
|
||||
export class TAP extends Base {}
|
||||
export class XUnit extends Base {
|
||||
constructor(runner: IRunner, options?: any);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "mocha" {
|
||||
export = Mocha;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +0,0 @@
|
|||
/// <reference path="es6-collections/es6-collections.d.ts" />
|
||||
/// <reference path="es6-promise/es6-promise.d.ts" />
|
||||
/// <reference path="mocha/mocha.d.ts" />
|
||||
/// <reference path="node/node.d.ts" />
|
||||
/// <reference path="source-map/source-map.d.ts" />
|
||||
/// <reference path="ws/ws.d.ts" />
|
|
@ -134,7 +134,7 @@ export class WebKitConnection {
|
|||
const responseArray = JSON.parse(jsonResponse);
|
||||
if (Array.isArray(responseArray)) {
|
||||
// Filter out extension targets and other things
|
||||
let pages = responseArray.filter(target => target && target.type === 'page');
|
||||
let pages = responseArray.filter(target => target && (!target.type || target.type === 'page'));
|
||||
|
||||
// If a url was specified (launch mode), try to filter to that url
|
||||
if (url) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -50,8 +50,8 @@ declare namespace WebKitProtocol {
|
|||
callFrames: CallFrame[];
|
||||
// 'exception' or 'other'
|
||||
reason: string;
|
||||
data: Runtime.RemoteObject;
|
||||
hitBreakpoints: BreakpointId[];
|
||||
data?: Runtime.RemoteObject;
|
||||
hitBreakpoints?: BreakpointId[];
|
||||
}
|
||||
|
||||
interface BreakpointResolvedParams {
|
||||
|
|
|
@ -11,11 +11,14 @@ var sourcemaps = require('gulp-sourcemaps');
|
|||
var mocha = require('gulp-mocha');
|
||||
|
||||
var sources = [
|
||||
'adapter',
|
||||
'common',
|
||||
'src',
|
||||
'test',
|
||||
'typings',
|
||||
'webkit',
|
||||
'debugger/adapter',
|
||||
'debugger/common',
|
||||
'debugger/cordova',
|
||||
'debugger/test',
|
||||
'debugger/webkit',
|
||||
].map(function(tsFolder) { return tsFolder + '/**/*.ts'; });
|
||||
|
||||
var projectConfig = {
|
||||
|
@ -43,14 +46,15 @@ gulp.task('default', ['build']);
|
|||
|
||||
// Don't lint code from tsd or common, and whitelist my files under adapter
|
||||
var lintSources = [
|
||||
'test',
|
||||
'webkit',
|
||||
'debugger/test',
|
||||
'debugger/webkit',
|
||||
'debugger/cordova',
|
||||
].map(function(tsFolder) { return tsFolder + '/**/*.ts'; });
|
||||
lintSources = lintSources.concat([
|
||||
'adapter/sourceMaps/sourceMapTransformer.ts',
|
||||
'adapter/adapterProxy.ts',
|
||||
'adapter/lineNumberTransformer.ts',
|
||||
'adapter/pathTransformer.ts',
|
||||
'debugger/adapter/sourceMaps/sourceMapTransformer.ts',
|
||||
'debugger/adapter/adapterProxy.ts',
|
||||
'debugger/adapter/lineNumberTransformer.ts',
|
||||
'debugger/adapter/pathTransformer.ts',
|
||||
]);
|
||||
|
||||
var tslint = require('gulp-tslint');
|
||||
|
@ -61,7 +65,7 @@ gulp.task('tslint', function(){
|
|||
});
|
||||
|
||||
function test() {
|
||||
return gulp.src('out/test/**/*.test.js', { read: false })
|
||||
return gulp.src('out/debugger/test/**/*.js', { read: false })
|
||||
.pipe(mocha({ ui: 'tdd' }))
|
||||
.on('error', function(e) {
|
||||
log(e ? e.toString() : 'error in test task!');
|
|
@ -0,0 +1,244 @@
|
|||
{
|
||||
"name": "vscode-cordova",
|
||||
"displayName": "vscode-cordova",
|
||||
"description": "VS Code Extension for Cordova applications",
|
||||
"version": "0.0.1",
|
||||
"publisher": "Microsoft",
|
||||
"engines": {
|
||||
"vscode": "^0.10.1"
|
||||
},
|
||||
"categories": [
|
||||
"Debuggers",
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:javascript",
|
||||
"onLanguage:typescript",
|
||||
"workspaceContains:config.xml"
|
||||
],
|
||||
"main": "./out/src/cordova",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "cordova.build",
|
||||
"title": "Cordova: Build"
|
||||
},
|
||||
{
|
||||
"command": "cordova.run",
|
||||
"title": "Cordova: Run"
|
||||
},
|
||||
{
|
||||
"command": "cordova.runDevice",
|
||||
"title": "Cordova: Run on a Device"
|
||||
},
|
||||
{
|
||||
"command": "cordova.emulate",
|
||||
"title": "Cordova: Run in an emulator"
|
||||
}
|
||||
],
|
||||
"debuggers": [
|
||||
{
|
||||
"type": "cordova",
|
||||
"label": "Cordova",
|
||||
"enableBreakpointsFor": {
|
||||
"languageIds": [
|
||||
"javascript",
|
||||
"typescriptreact"
|
||||
]
|
||||
},
|
||||
"program": "./out/debugger/cordova/debugCordova.js",
|
||||
"runtime": "node",
|
||||
"initialConfigurations": [
|
||||
{
|
||||
"name": "Run Android on device",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "android",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run iOS on device",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "ios",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running android on device",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "android",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running iOS on device",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "ios",
|
||||
"target": "device",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run Android on emulator",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "android",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Run iOS on emulator",
|
||||
"type": "cordova",
|
||||
"request": "launch",
|
||||
"platform": "ios",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running android on emulator",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "android",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach to running iOS on emulator",
|
||||
"type": "cordova",
|
||||
"request": "attach",
|
||||
"platform": "ios",
|
||||
"target": "emulator",
|
||||
"sourceMaps": true
|
||||
}
|
||||
],
|
||||
"configurationAttributes": {
|
||||
"launch": {
|
||||
"required": [
|
||||
"platform"
|
||||
],
|
||||
"properties": {
|
||||
"platform": {
|
||||
"type": "string",
|
||||
"description": "The platform to run on"
|
||||
},
|
||||
"target": {
|
||||
"type": "string",
|
||||
"description": "either 'device', 'emulator', or identifier for a specific device / emulator",
|
||||
"default": "emulator"
|
||||
},
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Use JavaScriptsource maps (if they exist)",
|
||||
"default": false
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to forward through to the target for debugging",
|
||||
"default": 9222
|
||||
},
|
||||
"webkitRangeMin": {
|
||||
"type": "number",
|
||||
"description": "Start of port range to use for iOS device selection",
|
||||
"default": 9223
|
||||
},
|
||||
"webkitRangeMax": {
|
||||
"type": "number",
|
||||
"description": "End of port range to use for iOS device selection",
|
||||
"default": 9322
|
||||
},
|
||||
"attachAttempts": {
|
||||
"type": "number",
|
||||
"description": "Number of attempts to make when attaching to an iOS app",
|
||||
"default": 5
|
||||
},
|
||||
"attachDelay": {
|
||||
"type": "number",
|
||||
"description": "Time in milliseconds to wait between attempts to attach to an iOS app",
|
||||
"default": 1000
|
||||
},
|
||||
"iosDebugProxyPort": {
|
||||
"type": "number",
|
||||
"description": "Port to use for connecting to iOS native debugger when launching app",
|
||||
"default": 9221
|
||||
},
|
||||
"appStepLaunchTimeout": {
|
||||
"type": "number",
|
||||
"description": "Timeout for individual steps when launching iOS app",
|
||||
"default": 5000
|
||||
}
|
||||
}
|
||||
},
|
||||
"attach": {
|
||||
"required": [],
|
||||
"properties": {
|
||||
"sourceMaps": {
|
||||
"type": "boolean",
|
||||
"description": "Use JavaScriptsource maps (if they exist)",
|
||||
"default": false
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to use for remote debugging.",
|
||||
"default": 9222
|
||||
},
|
||||
"webkitRangeMin": {
|
||||
"type": "number",
|
||||
"description": "Start of port range to use for iOS device selection",
|
||||
"default": 9223
|
||||
},
|
||||
"webkitRangeMax": {
|
||||
"type": "number",
|
||||
"description": "End of port range to use for iOS device selection",
|
||||
"default": 9322
|
||||
},
|
||||
"attachAttempts": {
|
||||
"type": "number",
|
||||
"description": "Number of attempts to make when attaching to an iOS app",
|
||||
"default": 5
|
||||
},
|
||||
"attachDelay": {
|
||||
"type": "number",
|
||||
"description": "Time in milliseconds to wait between attempts to attach to an iOS app",
|
||||
"default": 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "gulp",
|
||||
"compile": "gulp",
|
||||
"test": "node ./node_modules/mocha/bin/mocha --recursive -u tdd ./out/debugger/test/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-mocha": "^2.1.3",
|
||||
"gulp-sourcemaps": "^1.5.2",
|
||||
"gulp-tslint": "^3.3.1",
|
||||
"gulp-typescript": "^2.8.0",
|
||||
"gulp-util": "^3.0.5",
|
||||
"mocha": "^2.3.3",
|
||||
"mockery": "^1.4.0",
|
||||
"should": "^4.6.5",
|
||||
"sinon": "^1.17.2",
|
||||
"tslint": "^2.5.1",
|
||||
"rimraf": "^2.5.0",
|
||||
"typescript": "^1.6.2",
|
||||
"vscode": "0.10.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"elementtree": "^0.1.6",
|
||||
"plist-with-patches": "^0.5.1",
|
||||
"q": "^1.4.1",
|
||||
"request": "^2.67.0",
|
||||
"source-map": "^0.5.3",
|
||||
"ws": "0.8.0",
|
||||
"tsd": "^0.6.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"cordova-plugin-battery-status": {
|
||||
"queryString": "cordova/plugins:BatteryStatus",
|
||||
"typingFile": "cordova/plugins/BatteryStatus.d.ts"
|
||||
},
|
||||
"cordova-plugin-camera": {
|
||||
"queryString": "cordova/plugins:Camera",
|
||||
"typingFile": "cordova/plugins/Camera.d.ts"
|
||||
},
|
||||
"cordova-plugin-contacts": {
|
||||
"queryString": "cordova/plugins:Contact",
|
||||
"typingFile": "cordova/plugins/Contact.d.ts"
|
||||
},
|
||||
"cordova-plugin-device": {
|
||||
"queryString": "cordova/plugins:Device",
|
||||
"typingFile": "cordova/plugins/Device.d.ts"
|
||||
},
|
||||
"cordova-plugin-device-motion": {
|
||||
"queryString": "cordova/plugins:DeviceMotion",
|
||||
"typingFile": "cordova/plugins/DeviceMotion.d.ts"
|
||||
},
|
||||
"cordova-plugin-device-orientation": {
|
||||
"queryString": "cordova/plugins:DeviceOrientation",
|
||||
"typingFile": "cordova/plugins/DeviceOrientation.d.ts"
|
||||
},
|
||||
"cordova-plugin-dialogs": {
|
||||
"queryString": "cordova/plugins:Dialogs",
|
||||
"typingFile": "cordova/plugins/Dialogs.d.ts"
|
||||
},
|
||||
"cordova-plugin-file": {
|
||||
"queryString": "cordova/plugins:FileSystem",
|
||||
"typingFile": "cordova/plugins/FileSystem.d.ts"
|
||||
},
|
||||
"cordova-plugin-file-transfer": {
|
||||
"queryString": "cordova/plugins:FileTransfer",
|
||||
"typingFile": "cordova/plugins/FileTransfer.d.ts"
|
||||
},
|
||||
"cordova-plugin-globalization": {
|
||||
"queryString": "cordova/plugins:Globalization",
|
||||
"typingFile": "cordova/plugins/Globalization.d.ts"
|
||||
},
|
||||
"cordova-plugin-inappbrowser": {
|
||||
"queryString": "cordova/plugins:InAppBrowser",
|
||||
"typingFile": "cordova/plugins/InAppBrowser.d.ts"
|
||||
},
|
||||
"cordova-plugin-media": {
|
||||
"queryString": "cordova/plugins:Media",
|
||||
"typingFile": "cordova/plugins/Media.d.ts"
|
||||
},
|
||||
"cordova-plugin-media-capture": {
|
||||
"queryString": "cordova/plugins:MediaCapture",
|
||||
"typingFile": "cordova/plugins/MediaCapture.d.ts"
|
||||
},
|
||||
"cordova-plugin-network-information": {
|
||||
"queryString": "cordova/plugins:NetworkInformation",
|
||||
"typingFile": "cordova/plugins/NetworkInformation.d.ts"
|
||||
},
|
||||
"phonegap-plugin-push": {
|
||||
"queryString": "cordova/plugins:Push",
|
||||
"typingFile": "cordova/plugins/Push.d.ts"
|
||||
},
|
||||
"cordova-plugin-splashscreen": {
|
||||
"queryString": "cordova/plugins:Splashscreen",
|
||||
"typingFile": "cordova/plugins/Splashscreen.d.ts"
|
||||
},
|
||||
"cordova-plugin-statusbar": {
|
||||
"queryString": "cordova/plugins:StatusBar",
|
||||
"typingFile": "cordova/plugins/StatusBar.d.ts"
|
||||
},
|
||||
"cordova-plugin-vibration": {
|
||||
"queryString": "cordova/plugins:Vibration",
|
||||
"typingFile": "cordova/plugins/Vibration.d.ts"
|
||||
},
|
||||
"cordova-plugin-websql": {
|
||||
"queryString": "cordova/plugins:WebSQL",
|
||||
"typingFile": "cordova/plugins/WebSQL.d.ts"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import {TsdHelper} from './utils/tsdHelper';
|
||||
import {CordovaProjectHelper} from './utils/cordovaProjectHelper';
|
||||
import {CordovaCommandHelper} from './utils/CordovaCommandHelper';
|
||||
|
||||
let PLUGIN_TYPE_DEFS_FILENAME = "pluginTypings.json";
|
||||
let PLUGIN_TYPE_DEFS_PATH = path.resolve(__dirname, "..", "..", PLUGIN_TYPE_DEFS_FILENAME);
|
||||
let TSD_SETTINGS_JSON_FILE = "tsd.json";
|
||||
let EXTENSION_CONF_FOLDERNAME = "conf";
|
||||
let CORDOVA_TYPINGS_QUERYSTRING = "cordova";
|
||||
let TSD_JSON_INSTALLED_ATTR = "installed";
|
||||
|
||||
export function activate(context: vscode.ExtensionContext): void {
|
||||
// Get the project root and check if it is a Cordova project
|
||||
let cordovaProjectRoot = CordovaProjectHelper.getCordovaProjectRoot(vscode.workspace.rootPath);
|
||||
|
||||
if (!cordovaProjectRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to update the type definitions added to the project
|
||||
// as and when plugins are added or removed. For this reason,
|
||||
// setup a file system watcher to watch changes to plugins in the Cordova project
|
||||
// Note that watching plugins/fetch.json file would suffice
|
||||
|
||||
let watcher = vscode.workspace.createFileSystemWatcher('**/plugins/fetch.json', false, false, false); //dispose in deactivate
|
||||
watcher.ignoreChangeEvents = false;
|
||||
|
||||
watcher.onDidChange((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
|
||||
watcher.onDidDelete((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
|
||||
watcher.onDidCreate((e: vscode.Uri) => updatePluginTypeDefinitions(cordovaProjectRoot));
|
||||
|
||||
context.subscriptions.push(watcher);
|
||||
|
||||
// Register Cordova commands
|
||||
context.subscriptions.push(vscode.commands.registerCommand('cordova.build',
|
||||
() => CordovaCommandHelper.executeCordovaCommand(cordovaProjectRoot, "build")));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('cordova.run',
|
||||
() => CordovaCommandHelper.executeCordovaCommand(cordovaProjectRoot, "run")));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('cordova.runDevice',
|
||||
() => CordovaCommandHelper.executeCordovaCommand(cordovaProjectRoot, "run --device")));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('cordova.emulate',
|
||||
() => CordovaCommandHelper.executeCordovaCommand(cordovaProjectRoot, "emulate")));
|
||||
|
||||
let tsdJsonPath = getTsdSettingsFilePath(cordovaProjectRoot);
|
||||
|
||||
// Install the type defintion files for Cordova
|
||||
TsdHelper.installTypings(tsdJsonPath, [CORDOVA_TYPINGS_QUERYSTRING]);
|
||||
|
||||
// Install type definition files for the currently installed plugins
|
||||
updatePluginTypeDefinitions(cordovaProjectRoot);
|
||||
|
||||
}
|
||||
|
||||
function getTsdSettingsFilePath(cordovaProjectRoot: string): string {
|
||||
// Create the ".vscode" temp folder at the project root that will house the type definition files
|
||||
let typingsTargetPath = CordovaProjectHelper.getOrCreateTypingsTargetPath(cordovaProjectRoot);
|
||||
|
||||
let tsdJsonSrcPath = path.resolve(__dirname, "..", "..", EXTENSION_CONF_FOLDERNAME, TSD_SETTINGS_JSON_FILE);
|
||||
let tsdJsonDestPath = path.resolve(typingsTargetPath, TSD_SETTINGS_JSON_FILE);
|
||||
|
||||
// Copy tsd.json only if the project does not have one already
|
||||
if (fs.existsSync(tsdJsonSrcPath) && !fs.existsSync(tsdJsonDestPath)) {
|
||||
let tsdJsonContents = fs.readFileSync(tsdJsonSrcPath).toString();
|
||||
fs.writeFileSync(tsdJsonDestPath, tsdJsonContents);
|
||||
}
|
||||
|
||||
if (fs.existsSync(tsdJsonDestPath)) {
|
||||
return tsdJsonDestPath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getPluginTypingsJson() : any {
|
||||
if (!fs.existsSync(PLUGIN_TYPE_DEFS_PATH)) {
|
||||
console.error("Cordova plugin type declaration mapping file \"pluginTypings.json\" is missing from the extension folder.");
|
||||
return null;
|
||||
} else {
|
||||
return require(PLUGIN_TYPE_DEFS_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
function getNewTypeDefinitions(installedPlugins: string[]): string[] {
|
||||
let newTypeDefs: string[] = [];
|
||||
let pluginTypings = getPluginTypingsJson();
|
||||
if (!pluginTypings) {
|
||||
return;
|
||||
}
|
||||
|
||||
installedPlugins.forEach((pluginName: string) => {
|
||||
if (pluginTypings[pluginName]) {
|
||||
newTypeDefs.push(pluginTypings[pluginName].typingFile);
|
||||
}
|
||||
});
|
||||
|
||||
return newTypeDefs;
|
||||
}
|
||||
|
||||
function addPluginTypeDefinitions(installedPlugins: string[], currentTypeDefs: string[], tsdPath: string): void {
|
||||
let pluginTypings = getPluginTypingsJson();
|
||||
if (!pluginTypings) {
|
||||
return;
|
||||
}
|
||||
|
||||
let typingsToAdd = installedPlugins.filter((pluginName: string) => {
|
||||
if (pluginTypings[pluginName]) {
|
||||
return currentTypeDefs.indexOf(pluginTypings[pluginName].typingFile) < 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}).map((pluginName: string) => {
|
||||
return pluginTypings[pluginName].queryString;
|
||||
});
|
||||
|
||||
TsdHelper.installTypings(tsdPath, typingsToAdd);
|
||||
}
|
||||
|
||||
function removePluginTypeDefinitions(projectRoot: string, currentTypeDefs: string[], newTypeDefs: string[]): void {
|
||||
// Find the type definition files that need to be removed
|
||||
currentTypeDefs.forEach((typeDef: string) => {
|
||||
if (newTypeDefs.indexOf(typeDef) < 0) {
|
||||
var fileToDelete = path.resolve(CordovaProjectHelper.getCordovaPluginTypeDefsPath(projectRoot), typeDef);
|
||||
rimraf(fileToDelete, (err: Error) => {
|
||||
if (err) {
|
||||
// Debug-only message
|
||||
console.log("Failed to delete file " + fileToDelete);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatePluginTypeDefinitions(cordovaProjectRoot: string): void {
|
||||
let installedPlugins: string[] = CordovaProjectHelper.getInstalledPlugins(cordovaProjectRoot);
|
||||
let newTypeDefs = getNewTypeDefinitions(installedPlugins);
|
||||
let typeDefsFolder = CordovaProjectHelper.getCordovaPluginTypeDefsPath(cordovaProjectRoot);
|
||||
|
||||
if (!fs.existsSync(typeDefsFolder)) {
|
||||
addPluginTypeDefinitions(installedPlugins, [], CordovaProjectHelper.getTsdJsonPath(cordovaProjectRoot));
|
||||
return;
|
||||
}
|
||||
|
||||
fs.readdir(typeDefsFolder, (err: Error, currentTypeDefs: string[]) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
addPluginTypeDefinitions(installedPlugins, currentTypeDefs, CordovaProjectHelper.getTsdJsonPath(cordovaProjectRoot));
|
||||
removePluginTypeDefinitions(cordovaProjectRoot, currentTypeDefs, newTypeDefs);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import * as os from 'os';
|
||||
import {window} from 'vscode';
|
||||
|
||||
export class CordovaCommandHelper {
|
||||
private static CORDOVA_CMD_NAME = os.platform() === "darwin" ? "cordova" : "cordova.cmd";
|
||||
|
||||
public static executeCordovaCommand(projectRoot: string, command: string) {
|
||||
let outputChannel = window.createOutputChannel("cordova");
|
||||
let commandToExecute = CordovaCommandHelper.CORDOVA_CMD_NAME + " " + command;
|
||||
outputChannel.appendLine("########### EXECUTING: " + commandToExecute + " ###########");
|
||||
outputChannel.show();
|
||||
let process = child_process.exec(commandToExecute, {cwd: projectRoot});
|
||||
|
||||
process.on("error", (err: any) => {
|
||||
// ENOENT error will be thrown if no Cordova.cmd is found
|
||||
if (err.code === "ENOENT") {
|
||||
window.showErrorMessage("Cordova not found, please run 'npm install –g cordova' to install Cordova globally");
|
||||
}
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data: any) => {
|
||||
outputChannel.append(data);
|
||||
});
|
||||
|
||||
process.stdout.on('data', (data: any) => {
|
||||
outputChannel.append(data);
|
||||
});
|
||||
|
||||
process.stdout.on("close", (exitCode: number) => {
|
||||
outputChannel.appendLine("########### FINISHED EXECUTING : " + commandToExecute + " ###########");
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as Q from 'q';
|
||||
|
||||
export class CordovaProjectHelper {
|
||||
private static PROJECT_TYPINGS_FOLDERNAME = "typings";
|
||||
private static PROJECT_TYPINGS_PLUGINS_FOLDERNAME = "plugins";
|
||||
private static PROJECT_TYPINGS_CORDOVA_FOLDERNAME = "cordova";
|
||||
private static VSCODE_DIR: string = ".vscode";
|
||||
private static PLUGINS_FETCH_FILENAME: string = "fetch.json";
|
||||
private static CONFIG_XML_FILENAME: string = "config.xml";
|
||||
private static PROJECT_PLUGINS_DIR: string = "plugins";
|
||||
private static TSD_SETTINGS_JSON_FILE = "tsd.json";
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to get the list of plugins installed for the project.
|
||||
*/
|
||||
public static getInstalledPlugins(projectRoot: string): string[] {
|
||||
let fetchJsonPath: string = path.resolve(projectRoot, CordovaProjectHelper.PROJECT_PLUGINS_DIR, CordovaProjectHelper.PLUGINS_FETCH_FILENAME);
|
||||
|
||||
if (!fs.existsSync(fetchJsonPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
let fetchJsonContents = fs.readFileSync(fetchJsonPath).toString();
|
||||
let fetchJson = JSON.parse(fetchJsonContents);
|
||||
return Object.keys(fetchJson);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to find the root of the Cordova project. Returns null in the case of directories which are
|
||||
* not cordova-based projects. Otherwise, returns the project root path as a string.
|
||||
*/
|
||||
public static getCordovaProjectRoot(workspaceRoot: string): string {
|
||||
let parentPath: string;
|
||||
let projectRoot: string = workspaceRoot;
|
||||
let atFsRoot: boolean = false;
|
||||
while (fs.existsSync(projectRoot) && !fs.existsSync(path.join(projectRoot, CordovaProjectHelper.CONFIG_XML_FILENAME))) {
|
||||
// Navigate up one level until either taco.json is found or the parent path is invalid
|
||||
parentPath = path.resolve(projectRoot, "..");
|
||||
if (parentPath !== projectRoot) {
|
||||
projectRoot = parentPath;
|
||||
} else {
|
||||
// we have reached the filesystem root
|
||||
atFsRoot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (atFsRoot) {
|
||||
// We reached the fs root, so the project path passed was not a Cordova-based project directory
|
||||
return null;
|
||||
}
|
||||
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the target path for the type definition files (to be used for Cordova plugin intellisense).
|
||||
* Creates the target path if it does not exist already.
|
||||
*/
|
||||
public static getOrCreateTypingsTargetPath(projectRoot: string): string {
|
||||
let targetPath: string;
|
||||
if (projectRoot) {
|
||||
targetPath = path.resolve(projectRoot, CordovaProjectHelper.VSCODE_DIR);
|
||||
if(!fs.existsSync(targetPath)) {
|
||||
fs.mkdirSync(targetPath);
|
||||
}
|
||||
|
||||
return targetPath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the path of tsd.json file
|
||||
*/
|
||||
public static getTsdJsonPath(projectRoot: string): string {
|
||||
return path.resolve(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot), CordovaProjectHelper.TSD_SETTINGS_JSON_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the path to the Cordova type definitions folder
|
||||
*/
|
||||
public static getCordovaTypeDefsPath(projectRoot: string): string {
|
||||
return path.resolve(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the path to the plugin type definitions folder
|
||||
*/
|
||||
public static getCordovaPluginTypeDefsPath(projectRoot: string): string {
|
||||
return path.resolve(CordovaProjectHelper.getOrCreateTypingsTargetPath(projectRoot), CordovaProjectHelper.PROJECT_TYPINGS_FOLDERNAME, CordovaProjectHelper.PROJECT_TYPINGS_CORDOVA_FOLDERNAME, CordovaProjectHelper.PROJECT_TYPINGS_PLUGINS_FOLDERNAME);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as Q from 'q';
|
||||
import * as tsd from 'tsd';
|
||||
|
||||
export class TsdHelper {
|
||||
/**
|
||||
* Helper to install typings using tsd (TypeScript Definition Manager).
|
||||
* {tsdJsonPath} - the path to the tsd.json
|
||||
* {queryStrings} - the name-pattern query strings for type definition files to be installed
|
||||
*/
|
||||
public static installTypings(tsdJsonPath: string, queryStrings: string[]): void {
|
||||
let tsdApi = tsd.getAPI(tsdJsonPath);
|
||||
let query: tsd.Query;
|
||||
let options: tsd.Options;
|
||||
|
||||
tsdApi.readConfig(true).then(() => {
|
||||
let opts: tsd.Options = new tsd.Options();
|
||||
|
||||
let query = new tsd.Query();
|
||||
|
||||
// Add each of the name pattern strings passed as a
|
||||
// string array parameter to the query
|
||||
queryStrings.forEach((namePattern: string) => {
|
||||
query.addNamePattern(namePattern);
|
||||
});
|
||||
|
||||
query.versionMatcher = new tsd.VersionMatcher("latest");
|
||||
|
||||
return tsdApi.select(query, opts).then((selection: tsd.Selection) => {
|
||||
return tsdApi.install(selection, opts);
|
||||
});
|
||||
}).catch((err: Error): void => {
|
||||
// Catch all possible errors
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as Q from 'q';
|
||||
import * as rimraf from 'rimraf';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as testUtils from './testUtils';
|
||||
import {CordovaCommandHelper} from './../src/utils/CordovaCommandHelper';
|
||||
import {CordovaProjectHelper} from './../src/utils/CordovaProjectHelper';
|
||||
|
||||
suite("VSCode Cordova extension - intellisense and command palette tests", () => {
|
||||
let testProjectPath: string = path.resolve(__dirname, "..", "..", "test", "testProject");
|
||||
let vsCodeDir = path.resolve(testProjectPath, ".vscode");
|
||||
let cordovaTypeDef: string = CordovaProjectHelper.getCordovaTypeDefsPath(testProjectPath);
|
||||
|
||||
suiteTeardown(() => {
|
||||
// Cleanup the target folder for type definitions
|
||||
if (fs.existsSync(vsCodeDir)) {
|
||||
rimraf.sync(vsCodeDir);
|
||||
}
|
||||
});
|
||||
|
||||
function checkTypeDefinitions(expectedTypedDefs: string[])
|
||||
{
|
||||
let actualTypeDefs = testUtils.enumerateListOfTypeDefinitions(testProjectPath);
|
||||
assert.deepEqual(actualTypeDefs, expectedTypedDefs);
|
||||
};
|
||||
|
||||
test('Plugin type definitions are installed on activation', () => {
|
||||
return Q.delay(10000).then(() => {
|
||||
checkTypeDefinitions(["Camera.d.ts"]);
|
||||
});
|
||||
});
|
||||
|
||||
test('Plugin type defintion for a plugin is added upon adding that plugin', () => {
|
||||
return testUtils.addCordovaComponents("plugin", testProjectPath, ["cordova-plugin-device"])
|
||||
.then(() => {
|
||||
return Q.delay(10000);
|
||||
}).then(() => {
|
||||
checkTypeDefinitions(["Camera.d.ts", "Device.d.ts"]);
|
||||
});
|
||||
});
|
||||
|
||||
test('Plugin type definition for a plugin is removed after removal of that plugin', () => {
|
||||
return testUtils.removeCordovaComponents("plugin", testProjectPath, ["cordova-plugin-device"])
|
||||
.then(() => {
|
||||
return Q.delay(10000);
|
||||
}).then(() => {
|
||||
checkTypeDefinitions(["Camera.d.ts"]);
|
||||
});
|
||||
});
|
||||
|
||||
test('Verify that the commands registered by Cordova extension are loaded', () => {
|
||||
return vscode.commands.getCommands(true)
|
||||
.then((results) => {
|
||||
let cordovaCmdsAvailable = results.filter((commandName: string) => {
|
||||
return commandName.indexOf("cordova.") > -1
|
||||
});
|
||||
assert.deepEqual(cordovaCmdsAvailable, ["cordova.build", "cordova.run", "cordova.runDevice", "cordova.emulate"])
|
||||
});
|
||||
});
|
||||
|
||||
test('Execute Commands from the command palette', () => {
|
||||
return testUtils.addCordovaComponents("platform", testProjectPath, ["windows"])
|
||||
.then(() => {
|
||||
return vscode.commands.executeCommand("cordova.build");
|
||||
}).then(() => {
|
||||
return Q.delay(10000);
|
||||
}).then(res => {
|
||||
let appxPackagesParentPath = path.resolve(testProjectPath, "platforms", "windows", "AppPackages");
|
||||
assert.ok(fs.existsSync(appxPackagesParentPath));
|
||||
return testUtils.removeCordovaComponents("platform", testProjectPath, ["windows"])
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
//
|
||||
// This file is providing the test runner to use when running extension tests.
|
||||
// By default the test runner in use is Mocha based.
|
||||
//
|
||||
// You can provide your own test runner if you want to override it by exporting
|
||||
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
|
||||
// host can call to run the tests. The test runner is expected to use console.log
|
||||
// to report the results back to the caller. When the tests are finished, return
|
||||
// a possible error to the callback or null if none.
|
||||
|
||||
var testRunner = require('vscode/lib/testrunner');
|
||||
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true, // colored output from test results
|
||||
timeout: 150000
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="io.cordova.hellocordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<name>HelloCordova</name>
|
||||
<description>
|
||||
A sample Apache Cordova application that responds to the deviceready event.
|
||||
</description>
|
||||
<author email="dev@cordova.apache.org" href="http://cordova.io">
|
||||
Apache Cordova Team
|
||||
</author>
|
||||
<content src="index.html" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="1" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
</widget>
|
|
@ -0,0 +1,23 @@
|
|||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-->
|
||||
# Cordova Hooks
|
||||
|
||||
Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide.
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
* {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
|
||||
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
|
||||
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
|
||||
background-color:#E4E4E4;
|
||||
background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0, #A7A7A7),
|
||||
color-stop(0.51, #E4E4E4)
|
||||
);
|
||||
background-attachment:fixed;
|
||||
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
font-size:12px;
|
||||
height:100%;
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
text-transform:uppercase;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
/* Portrait layout (default) */
|
||||
.app {
|
||||
background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
|
||||
position:absolute; /* position in the center of the screen */
|
||||
left:50%;
|
||||
top:50%;
|
||||
height:50px; /* text area height */
|
||||
width:225px; /* text area width */
|
||||
text-align:center;
|
||||
padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */
|
||||
margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */
|
||||
/* offset horizontal: half of text area width */
|
||||
}
|
||||
|
||||
/* Landscape layout (with min-width) */
|
||||
@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
|
||||
.app {
|
||||
background-position:left center;
|
||||
padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */
|
||||
margin:-90px 0px 0px -198px; /* offset vertical: half of image height */
|
||||
/* offset horizontal: half of image width and text area width */
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:24px;
|
||||
font-weight:normal;
|
||||
margin:0px;
|
||||
overflow:visible;
|
||||
padding:0px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.event {
|
||||
border-radius:4px;
|
||||
-webkit-border-radius:4px;
|
||||
color:#FFFFFF;
|
||||
font-size:12px;
|
||||
margin:0px 30px;
|
||||
padding:2px 0px;
|
||||
}
|
||||
|
||||
.event.listening {
|
||||
background-color:#333333;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.event.received {
|
||||
background-color:#4B946A;
|
||||
display:none;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation:fade 3000ms infinite;
|
||||
-webkit-animation:fade 3000ms infinite;
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 21 KiB |
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
Customize this policy to fit your own app's needs. For more guidance, see:
|
||||
https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
|
||||
Some notes:
|
||||
* gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
|
||||
* https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
|
||||
* Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
|
||||
* Enable inline JS: add 'unsafe-inline' to default-src
|
||||
-->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css">
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<h1>Apache Cordova</h1>
|
||||
<div id="deviceready" class="blink">
|
||||
<p class="event listening">Connecting to Device</p>
|
||||
<p class="event received">Device is Ready</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="cordova.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function() {
|
||||
this.bindEvents();
|
||||
},
|
||||
// Bind Event Listeners
|
||||
//
|
||||
// Bind any events that are required on startup. Common events are:
|
||||
// 'load', 'deviceready', 'offline', and 'online'.
|
||||
bindEvents: function() {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
// deviceready Event Handler
|
||||
//
|
||||
// The scope of 'this' is the event. In order to call the 'receivedEvent'
|
||||
// function, we must explicitly call 'app.receivedEvent(...);'
|
||||
onDeviceReady: function() {
|
||||
app.receivedEvent('deviceready');
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedEvent: function(id) {
|
||||
var parentElement = document.getElementById(id);
|
||||
var listeningElement = parentElement.querySelector('.listening');
|
||||
var receivedElement = parentElement.querySelector('.received');
|
||||
|
||||
listeningElement.setAttribute('style', 'display:none;');
|
||||
receivedElement.setAttribute('style', 'display:block;');
|
||||
|
||||
console.log('Received Event: ' + id);
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as Q from 'q';
|
||||
|
||||
import {CordovaCommandHelper} from './../src/utils/CordovaCommandHelper';
|
||||
import {CordovaProjectHelper} from './../src/utils/CordovaProjectHelper';
|
||||
|
||||
|
||||
export function executeCordovaCommand(cwd: string, command: string): Q.Promise<any> {
|
||||
let deferred = Q.defer<any>();
|
||||
let cordovaCmd = os.platform() === "darwin" ? "cordova" : "cordova.cmd";
|
||||
let commandToExecute = cordovaCmd + " " + command;
|
||||
let process = child_process.exec(commandToExecute, {cwd: cwd});
|
||||
|
||||
process.on("error", function(err: any): void {
|
||||
// ENOENT error will be thrown if no Cordova.cmd is found
|
||||
deferred.reject(err);
|
||||
});
|
||||
process.stdout.on("close", exitCode => {
|
||||
if (exitCode) {
|
||||
deferred.reject("Cordova command failed with exit code " + exitCode);
|
||||
} else {
|
||||
deferred.resolve({});
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
export function createCordovaProject(cwd: string, projectName: string): Q.Promise<any> {
|
||||
let cordovaCommandToRun = "create " + projectName;
|
||||
return executeCordovaCommand(cwd, cordovaCommandToRun);
|
||||
}
|
||||
|
||||
export function addCordovaComponents(componentName: string, projectRoot: string, componentsToAdd: string[]): Q.Promise<any> {
|
||||
let cordovaCommandToRun = componentName + " add " + componentsToAdd.join(" ");
|
||||
return executeCordovaCommand(projectRoot, cordovaCommandToRun);
|
||||
}
|
||||
|
||||
export function removeCordovaComponents(componentName: string, projectRoot: string, componentsToRemove: string[]): Q.Promise<any> {
|
||||
let cordovaCommandToRun = componentName + " remove " + componentsToRemove.join(" ");
|
||||
return executeCordovaCommand(projectRoot, cordovaCommandToRun);
|
||||
}
|
||||
|
||||
export function enumerateListOfTypeDefinitions(projectRoot: string): string[] {
|
||||
let typeDefsFolder = CordovaProjectHelper.getCordovaPluginTypeDefsPath(projectRoot);
|
||||
|
||||
// look for all the type defs in the typings folder
|
||||
if (!fs.existsSync(typeDefsFolder)) {
|
||||
return [];
|
||||
} else {
|
||||
return fs.readdirSync(typeDefsFolder);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,6 @@
|
|||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"testapp/node_modules"
|
||||
"debugger/testapp/node_modules"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
// Barebones typing for elementtree, added as-needed
|
||||
|
||||
declare module "elementtree" {
|
||||
export class ElementTree {
|
||||
constructor(xml: XMLElement);
|
||||
|
||||
getroot(): XMLElement
|
||||
find(name: string): XMLElement;
|
||||
findall(name: string): XMLElement[];
|
||||
}
|
||||
|
||||
export class XMLElement {
|
||||
attrib: { [key: string]: string };
|
||||
text: string;
|
||||
tag: string;
|
||||
_children: XMLElement[];
|
||||
}
|
||||
|
||||
export function XML(data: string): XMLElement;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Type definitions for form-data
|
||||
// Project: https://github.com/felixge/node-form-data
|
||||
// Definitions by: Carlos Ballesteros Velasco <https://github.com/soywiz>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
// Imported from: https://github.com/soywiz/typescript-node-definitions/form-data.d.ts
|
||||
|
||||
declare module "form-data" {
|
||||
export class FormData {
|
||||
append(key: string, value: any, options?: any): FormData;
|
||||
getHeaders(): Object;
|
||||
// TODO expand pipe
|
||||
pipe(to: any): any;
|
||||
submit(params: string|Object, callback: (error: any, response: any) => void): any;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/// <reference path="../../node_modules/vscode/typings/node.d.ts" />
|
|
@ -5,4 +5,4 @@ interface String {
|
|||
* position. Otherwise returns false.
|
||||
*/
|
||||
startsWith(searchString: string, position?: number): boolean;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*******************************************************
|
||||
* *
|
||||
* Copyright (C) Microsoft. All rights reserved. *
|
||||
* *
|
||||
*******************************************************
|
||||
*/
|
||||
|
||||
// Barebones typing for plist-with-patches, added as-needed
|
||||
|
||||
declare module "plist-with-patches" {
|
||||
export function parseFileSync(filename: string): any;
|
||||
|
||||
/**
|
||||
* generate an XML plist string from the input object
|
||||
*
|
||||
* @param object obj the object to convert
|
||||
* @return string converted plist
|
||||
*/
|
||||
export function build(obj: any): string;
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
// Type definitions for Q
|
||||
// Project: https://github.com/kriskowal/q
|
||||
// Definitions by: Barrie Nemetchek <https://github.com/bnemetchek>, Andrew Gaspar <https://github.com/AndrewGaspar/>, John Reilly <https://github.com/johnnyreilly>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/**
|
||||
* If value is a Q promise, returns the promise.
|
||||
* If value is a promise from another library it is coerced into a Q promise (where possible).
|
||||
*/
|
||||
declare function Q<T>(promise: Q.IPromise<T>): Q.Promise<T>;
|
||||
/**
|
||||
* If value is not a promise, returns a promise that is fulfilled with value.
|
||||
*/
|
||||
declare function Q<T>(value: T): Q.Promise<T>;
|
||||
|
||||
declare module Q {
|
||||
interface IPromise<T> {
|
||||
then<U>(onFulfill?: (value: T) => U | IPromise<U>, onReject?: (error: any) => U | IPromise<U>): IPromise<U>;
|
||||
}
|
||||
|
||||
interface Deferred<T> {
|
||||
promise: Promise<T>;
|
||||
resolve(value: T): void;
|
||||
reject(reason: any): void;
|
||||
notify(value: any): void;
|
||||
makeNodeResolver(): (reason: any, value: T) => void;
|
||||
}
|
||||
|
||||
interface Promise<T> {
|
||||
/**
|
||||
* Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object.
|
||||
|
||||
* finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished.
|
||||
*/
|
||||
fin(finallyCallback: () => any): Promise<T>;
|
||||
/**
|
||||
* Like a finally clause, allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful for collecting resources regardless of whether a job succeeded, like closing a database connection, shutting a server down, or deleting an unneeded key from an object.
|
||||
|
||||
* finally returns a promise, which will become resolved with the same fulfillment value or rejection reason as promise. However, if callback returns a promise, the resolution of the returned promise will be delayed until the promise returned from callback is finished.
|
||||
*/
|
||||
finally(finallyCallback: () => any): Promise<T>;
|
||||
|
||||
/**
|
||||
* The then method from the Promises/A+ specification, with an additional progress handler.
|
||||
*/
|
||||
then<U>(onFulfill?: (value: T) => U | IPromise<U>, onReject?: (error: any) => U | IPromise<U>, onProgress?: Function): Promise<U>;
|
||||
|
||||
/**
|
||||
* Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason.
|
||||
*
|
||||
* This is especially useful in conjunction with all
|
||||
*/
|
||||
spread<U>(onFulfill: (...args: any[]) => IPromise<U> | U, onReject?: (reason: any) => IPromise<U> | U): Promise<U>;
|
||||
|
||||
fail<U>(onRejected: (reason: any) => U | IPromise<U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* A sugar method, equivalent to promise.then(undefined, onRejected).
|
||||
*/
|
||||
catch<U>(onRejected: (reason: any) => U | IPromise<U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* A sugar method, equivalent to promise.then(undefined, undefined, onProgress).
|
||||
*/
|
||||
progress(onProgress: (progress: any) => any): Promise<T>;
|
||||
|
||||
/**
|
||||
* Much like then, but with different behavior around unhandled rejection. If there is an unhandled rejection, either because promise is rejected and no onRejected callback was provided, or because onFulfilled or onRejected threw an error or returned a rejected promise, the resulting rejection reason is thrown as an exception in a future turn of the event loop.
|
||||
*
|
||||
* This method should be used to terminate chains of promises that will not be passed elsewhere. Since exceptions thrown in then callbacks are consumed and transformed into rejections, exceptions at the end of the chain are easy to accidentally, silently ignore. By arranging for the exception to be thrown in a future turn of the event loop, so that it won't be caught, it causes an onerror event on the browser window, or an uncaughtException event on Node.js's process object.
|
||||
*
|
||||
* Exceptions thrown by done will have long stack traces, if Q.longStackSupport is set to true. If Q.onerror is set, exceptions will be delivered there instead of thrown in a future turn.
|
||||
*
|
||||
* The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it.
|
||||
*/
|
||||
done(onFulfilled?: (value: T) => any, onRejected?: (reason: any) => any, onProgress?: (progress: any) => any): void;
|
||||
|
||||
/**
|
||||
* If callback is a function, assumes it's a Node.js-style callback, and calls it as either callback(rejectionReason) when/if promise becomes rejected, or as callback(null, fulfillmentValue) when/if promise becomes fulfilled. If callback is not a function, simply returns promise.
|
||||
*/
|
||||
nodeify(callback: (reason: any, value: any) => void): Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns a promise to get the named property of an object. Essentially equivalent to
|
||||
*
|
||||
* promise.then(function (o) {
|
||||
* return o[propertyName];
|
||||
* });
|
||||
*/
|
||||
get<U>(propertyName: String): Promise<U>;
|
||||
set<U>(propertyName: String, value: any): Promise<U>;
|
||||
delete<U>(propertyName: String): Promise<U>;
|
||||
/**
|
||||
* Returns a promise for the result of calling the named method of an object with the given array of arguments. The object itself is this in the function, just like a synchronous method call. Essentially equivalent to
|
||||
*
|
||||
* promise.then(function (o) {
|
||||
* return o[methodName].apply(o, args);
|
||||
* });
|
||||
*/
|
||||
post<U>(methodName: String, args: any[]): Promise<U>;
|
||||
/**
|
||||
* Returns a promise for the result of calling the named method of an object with the given variadic arguments. The object itself is this in the function, just like a synchronous method call.
|
||||
*/
|
||||
invoke<U>(methodName: String, ...args: any[]): Promise<U>;
|
||||
fapply<U>(args: any[]): Promise<U>;
|
||||
fcall<U>(...args: any[]): Promise<U>;
|
||||
|
||||
/**
|
||||
* Returns a promise for an array of the property names of an object. Essentially equivalent to
|
||||
*
|
||||
* promise.then(function (o) {
|
||||
* return Object.keys(o);
|
||||
* });
|
||||
*/
|
||||
keys(): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* A sugar method, equivalent to promise.then(function () { return value; }).
|
||||
*/
|
||||
thenResolve<U>(value: U): Promise<U>;
|
||||
/**
|
||||
* A sugar method, equivalent to promise.then(function () { throw reason; }).
|
||||
*/
|
||||
thenReject(reason: any): Promise<T>;
|
||||
|
||||
/**
|
||||
* Attaches a handler that will observe the value of the promise when it becomes fulfilled, returning a promise for that same value, perhaps deferred but not replaced by the promise returned by the onFulfilled handler.
|
||||
*/
|
||||
tap(onFulfilled: (value: T) => any): Promise<T>;
|
||||
|
||||
timeout(ms: number, message?: string): Promise<T>;
|
||||
/**
|
||||
* Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed.
|
||||
*/
|
||||
delay(ms: number): Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true.
|
||||
*/
|
||||
isFulfilled(): boolean;
|
||||
/**
|
||||
* Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false.
|
||||
*/
|
||||
isRejected(): boolean;
|
||||
/**
|
||||
* Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false.
|
||||
*/
|
||||
isPending(): boolean;
|
||||
|
||||
valueOf(): any;
|
||||
|
||||
/**
|
||||
* Returns a "state snapshot" object, which will be in one of three forms:
|
||||
*
|
||||
* - { state: "pending" }
|
||||
* - { state: "fulfilled", value: <fulfllment value> }
|
||||
* - { state: "rejected", reason: <rejection reason> }
|
||||
*/
|
||||
inspect(): PromiseState<T>;
|
||||
}
|
||||
|
||||
interface PromiseState<T> {
|
||||
/**
|
||||
* "fulfilled", "rejected", "pending"
|
||||
*/
|
||||
state: string;
|
||||
value?: T;
|
||||
reason?: any;
|
||||
}
|
||||
|
||||
// If no value provided, returned promise will be of void type
|
||||
export function when(): Promise<void>;
|
||||
|
||||
// if no fulfill, reject, or progress provided, returned promise will be of same type
|
||||
export function when<T>(value: T | IPromise<T>): Promise<T>;
|
||||
|
||||
// If a non-promise value is provided, it will not reject or progress
|
||||
export function when<T, U>(value: T | IPromise<T>, onFulfilled: (val: T) => U | IPromise<U>, onRejected?: (reason: any) => U | IPromise<U>, onProgress?: (progress: any) => any): Promise<U>;
|
||||
|
||||
/**
|
||||
* Currently "impossible" (and I use the term loosely) to implement due to TypeScript limitations as it is now.
|
||||
* See: https://github.com/Microsoft/TypeScript/issues/1784 for discussion on it.
|
||||
*/
|
||||
// export function try(method: Function, ...args: any[]): Promise<any>;
|
||||
|
||||
export function fbind<T>(method: (...args: any[]) => T | IPromise<T>, ...args: any[]): (...args: any[]) => Promise<T>;
|
||||
|
||||
export function fcall<T>(method: (...args: any[]) => T, ...args: any[]): Promise<T>;
|
||||
|
||||
export function send<T>(obj: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
export function invoke<T>(obj: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
export function mcall<T>(obj: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
|
||||
export function denodeify<T>(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise<T>;
|
||||
export function nbind<T>(nodeFunction: Function, thisArg: any, ...args: any[]): (...args: any[]) => Promise<T>;
|
||||
export function nfbind<T>(nodeFunction: Function, ...args: any[]): (...args: any[]) => Promise<T>;
|
||||
export function nfcall<T>(nodeFunction: Function, ...args: any[]): Promise<T>;
|
||||
export function nfapply<T>(nodeFunction: Function, args: any[]): Promise<T>;
|
||||
|
||||
export function ninvoke<T>(nodeModule: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
export function npost<T>(nodeModule: any, functionName: string, args: any[]): Promise<T>;
|
||||
export function nsend<T>(nodeModule: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
export function nmcall<T>(nodeModule: any, functionName: string, ...args: any[]): Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected.
|
||||
*/
|
||||
export function all<T>(promises: IPromise<T>[]): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* Returns a promise for the first of an array of promises to become settled.
|
||||
*/
|
||||
export function race<T>(promises: IPromise<T>[]): Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected.
|
||||
*/
|
||||
export function allSettled<T>(promises: IPromise<T>[]): Promise<PromiseState<T>[]>;
|
||||
|
||||
export function allResolved<T>(promises: IPromise<T>[]): Promise<Promise<T>[]>;
|
||||
|
||||
/**
|
||||
* Like then, but "spreads" the array into a variadic fulfillment handler. If any of the promises in the array are rejected, instead calls onRejected with the first rejected promise's rejection reason.
|
||||
* This is especially useful in conjunction with all.
|
||||
*/
|
||||
export function spread<T, U>(promises: IPromise<T>[], onFulfilled: (...args: T[]) => U | IPromise<U>, onRejected?: (reason: any) => U | IPromise<U>): Promise<U>;
|
||||
|
||||
/**
|
||||
* Returns a promise that will have the same result as promise, except that if promise is not fulfilled or rejected before ms milliseconds, the returned promise will be rejected with an Error with the given message. If message is not supplied, the message will be "Timed out after " + ms + " ms".
|
||||
*/
|
||||
export function timeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed.
|
||||
*/
|
||||
export function delay<T>(promise: Promise<T>, ms: number): Promise<T>;
|
||||
/**
|
||||
* Returns a promise that will have the same result as promise, but will only be fulfilled or rejected after at least ms milliseconds have passed.
|
||||
*/
|
||||
export function delay<T>(value: T, ms: number): Promise<T>;
|
||||
/**
|
||||
* Returns a promise that will be fulfilled with undefined after at least ms milliseconds have passed.
|
||||
*/
|
||||
export function delay(ms: number): Promise <void>;
|
||||
/**
|
||||
* Returns whether a given promise is in the fulfilled state. When the static version is used on non-promises, the result is always true.
|
||||
*/
|
||||
export function isFulfilled(promise: Promise<any>): boolean;
|
||||
/**
|
||||
* Returns whether a given promise is in the rejected state. When the static version is used on non-promises, the result is always false.
|
||||
*/
|
||||
export function isRejected(promise: Promise<any>): boolean;
|
||||
/**
|
||||
* Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false.
|
||||
*/
|
||||
export function isPending(promise: Promise<any>): boolean;
|
||||
|
||||
/**
|
||||
* Returns a "deferred" object with a:
|
||||
* promise property
|
||||
* resolve(value) method
|
||||
* reject(reason) method
|
||||
* notify(value) method
|
||||
* makeNodeResolver() method
|
||||
*/
|
||||
export function defer<T>(): Deferred<T>;
|
||||
|
||||
/**
|
||||
* Returns a promise that is rejected with reason.
|
||||
*/
|
||||
export function reject<T>(reason?: any): Promise<T>;
|
||||
|
||||
export function Promise<T>(resolver: (resolve: (val: T | IPromise<T>) => void , reject: (reason: any) => void , notify: (progress: any) => void ) => void ): Promise<T>;
|
||||
|
||||
/**
|
||||
* Creates a new version of func that accepts any combination of promise and non-promise values, converting them to their fulfillment values before calling the original func. The returned version also always returns a promise: if func does a return or throw, then Q.promised(func) will return fulfilled or rejected promise, respectively.
|
||||
*
|
||||
* This can be useful for creating functions that accept either promises or non-promise values, and for ensuring that the function always returns a promise even in the face of unintentional thrown exceptions.
|
||||
*/
|
||||
export function promised<T>(callback: (...args: any[]) => T): (...args: any[]) => Promise<T>;
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a Q promise.
|
||||
*/
|
||||
export function isPromise(object: any): boolean;
|
||||
/**
|
||||
* Returns whether the given value is a promise (i.e. it's an object with a then function).
|
||||
*/
|
||||
export function isPromiseAlike(object: any): boolean;
|
||||
/**
|
||||
* Returns whether a given promise is in the pending state. When the static version is used on non-promises, the result is always false.
|
||||
*/
|
||||
export function isPending(object: any): boolean;
|
||||
|
||||
/**
|
||||
* This is an experimental tool for converting a generator function into a deferred function. This has the potential of reducing nested callbacks in engines that support yield.
|
||||
*/
|
||||
export function async<T>(generatorFunction: any): (...args: any[]) => Promise<T>;
|
||||
export function nextTick(callback: Function): void;
|
||||
|
||||
/**
|
||||
* A settable property that will intercept any uncaught errors that would otherwise be thrown in the next tick of the event loop, usually as a result of done. Can be useful for getting the full stack trace of an error in browsers, which is not usually possible with window.onerror.
|
||||
*/
|
||||
export var onerror: (reason: any) => void;
|
||||
/**
|
||||
* A settable property that lets you turn on long stack trace support. If turned on, "stack jumps" will be tracked across asynchronous promise operations, so that if an uncaught error is thrown by done or a rejection reason's stack property is inspected in a rejection callback, a long stack trace is produced.
|
||||
*/
|
||||
export var longStackSupport: boolean;
|
||||
|
||||
/**
|
||||
* Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does).
|
||||
* Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason.
|
||||
* Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value.
|
||||
* Calling resolve with a non-promise value causes promise to be fulfilled with that value.
|
||||
*/
|
||||
export function resolve<T>(object: IPromise<T>): Promise<T>;
|
||||
/**
|
||||
* Calling resolve with a pending promise causes promise to wait on the passed promise, becoming fulfilled with its fulfillment value or rejected with its rejection reason (or staying pending forever, if the passed promise does).
|
||||
* Calling resolve with a rejected promise causes promise to be rejected with the passed promise's rejection reason.
|
||||
* Calling resolve with a fulfilled promise causes promise to be fulfilled with the passed promise's fulfillment value.
|
||||
* Calling resolve with a non-promise value causes promise to be fulfilled with that value.
|
||||
*/
|
||||
export function resolve<T>(object: T): Promise<T>;
|
||||
|
||||
/**
|
||||
* Resets the global "Q" variable to the value it has before Q was loaded.
|
||||
* This will either be undefined if there was no version or the version of Q which was already loaded before.
|
||||
* @returns { The last version of Q. }
|
||||
*/
|
||||
export function noConflict(): typeof Q;
|
||||
}
|
||||
|
||||
declare module "q" {
|
||||
export = Q;
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// Type definitions for request
|
||||
// Project: https://github.com/mikeal/request
|
||||
// Definitions by: Carlos Ballesteros Velasco <https://github.com/soywiz>, bonnici <https://github.com/bonnici>, Bart van der Schoor <https://github.com/Bartvds>, Joe Skeen <http://github.com/joeskeen>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
// Imported from: https://github.com/soywiz/typescript-node-definitions/d.ts
|
||||
|
||||
/// <reference path="../node/node.d.ts" />
|
||||
/// <reference path="../form-data/form-data.d.ts" />
|
||||
|
||||
declare module 'request' {
|
||||
import stream = require('stream');
|
||||
import http = require('http');
|
||||
import FormData = require('form-data');
|
||||
import url = require('url');
|
||||
import fs = require('fs');
|
||||
|
||||
namespace request {
|
||||
export interface RequestAPI<TRequest extends Request,
|
||||
TOptions extends CoreOptions,
|
||||
TUriUrlOptions> {
|
||||
|
||||
defaults(options: TOptions): RequestAPI<TRequest, TOptions, RequiredUriUrl>;
|
||||
defaults(options: RequiredUriUrl & TOptions): DefaultUriUrlRequestApi<TRequest, TOptions, OptionalUriUrl>;
|
||||
|
||||
(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
(uri: string, callback?: RequestCallback): TRequest;
|
||||
(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
get(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
get(uri: string, callback?: RequestCallback): TRequest;
|
||||
get(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
post(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
post(uri: string, callback?: RequestCallback): TRequest;
|
||||
post(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
put(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
put(uri: string, callback?: RequestCallback): TRequest;
|
||||
put(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
head(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
head(uri: string, callback?: RequestCallback): TRequest;
|
||||
head(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
patch(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
patch(uri: string, callback?: RequestCallback): TRequest;
|
||||
patch(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
del(uri: string, options?: TOptions, callback?: RequestCallback): TRequest;
|
||||
del(uri: string, callback?: RequestCallback): TRequest;
|
||||
del(options: TUriUrlOptions & TOptions, callback?: RequestCallback): TRequest;
|
||||
|
||||
forever(agentOptions: any, optionsArg: any): TRequest;
|
||||
jar(): CookieJar;
|
||||
cookie(str: string): Cookie;
|
||||
|
||||
initParams: any;
|
||||
debug: boolean;
|
||||
}
|
||||
|
||||
interface DefaultUriUrlRequestApi<TRequest extends Request,
|
||||
TOptions extends CoreOptions,
|
||||
TUriUrlOptions> extends RequestAPI<TRequest, TOptions, TUriUrlOptions> {
|
||||
|
||||
defaults(options: TOptions): DefaultUriUrlRequestApi<TRequest, TOptions, OptionalUriUrl>;
|
||||
(): TRequest;
|
||||
get(): TRequest;
|
||||
post(): TRequest;
|
||||
put(): TRequest;
|
||||
head(): TRequest;
|
||||
patch(): TRequest;
|
||||
del(): TRequest;
|
||||
}
|
||||
|
||||
interface CoreOptions {
|
||||
baseUrl?: string;
|
||||
callback?: (error: any, response: http.IncomingMessage, body: any) => void;
|
||||
jar?: any; // CookieJar
|
||||
formData?: any; // Object
|
||||
form?: any; // Object or string
|
||||
auth?: AuthOptions;
|
||||
oauth?: OAuthOptions;
|
||||
aws?: AWSOptions;
|
||||
hawk?: HawkOptions;
|
||||
qs?: any;
|
||||
json?: any;
|
||||
multipart?: RequestPart[] | Multipart;
|
||||
agentOptions?: any;
|
||||
agentClass?: any;
|
||||
forever?: any;
|
||||
host?: string;
|
||||
port?: number;
|
||||
method?: string;
|
||||
headers?: Headers;
|
||||
body?: any;
|
||||
followRedirect?: boolean | ((response: http.IncomingMessage) => boolean);
|
||||
followAllRedirects?: boolean;
|
||||
maxRedirects?: number;
|
||||
encoding?: string;
|
||||
pool?: any;
|
||||
timeout?: number;
|
||||
proxy?: any;
|
||||
strictSSL?: boolean;
|
||||
gzip?: boolean;
|
||||
preambleCRLF?: boolean;
|
||||
postambleCRLF?: boolean;
|
||||
key?: Buffer;
|
||||
cert?: Buffer;
|
||||
passphrase?: string;
|
||||
ca?: Buffer;
|
||||
har?: HttpArchiveRequest;
|
||||
}
|
||||
|
||||
interface UriOptions {
|
||||
uri: string;
|
||||
}
|
||||
interface UrlOptions {
|
||||
url: string;
|
||||
}
|
||||
export type RequiredUriUrl = UriOptions | UrlOptions;
|
||||
|
||||
interface OptionalUriUrl {
|
||||
uri?: string;
|
||||
url?: string;
|
||||
}
|
||||
export type Options = RequiredUriUrl & CoreOptions;
|
||||
|
||||
export interface RequestCallback {
|
||||
(error: any, response: http.IncomingMessage, body: any): void;
|
||||
}
|
||||
|
||||
export interface HttpArchiveRequest {
|
||||
url?: string;
|
||||
method?: string;
|
||||
headers?: NameValuePair[];
|
||||
postData?: {
|
||||
mimeType?: string;
|
||||
params?: NameValuePair[];
|
||||
}
|
||||
}
|
||||
|
||||
export interface NameValuePair {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Multipart {
|
||||
chunked?: boolean;
|
||||
data?: {
|
||||
'content-type'?: string,
|
||||
body: string
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface RequestPart {
|
||||
headers?: Headers;
|
||||
body: any;
|
||||
}
|
||||
|
||||
export interface Request extends stream.Stream {
|
||||
readable: boolean;
|
||||
writable: boolean;
|
||||
|
||||
getAgent(): http.Agent;
|
||||
//start(): void;
|
||||
//abort(): void;
|
||||
pipeDest(dest: any): void;
|
||||
setHeader(name: string, value: string, clobber?: boolean): Request;
|
||||
setHeaders(headers: Headers): Request;
|
||||
qs(q: Object, clobber?: boolean): Request;
|
||||
form(): FormData.FormData;
|
||||
form(form: any): Request;
|
||||
multipart(multipart: RequestPart[]): Request;
|
||||
json(val: any): Request;
|
||||
aws(opts: AWSOptions, now?: boolean): Request;
|
||||
auth(username: string, password: string, sendInmediately?: boolean, bearer?: string): Request;
|
||||
oauth(oauth: OAuthOptions): Request;
|
||||
jar(jar: CookieJar): Request;
|
||||
|
||||
on(event: string, listener: Function): Request;
|
||||
|
||||
write(buffer: Buffer, cb?: Function): boolean;
|
||||
write(str: string, cb?: Function): boolean;
|
||||
write(str: string, encoding: string, cb?: Function): boolean;
|
||||
write(str: string, encoding?: string, fd?: string): boolean;
|
||||
end(): void;
|
||||
end(chunk: Buffer, cb?: Function): void;
|
||||
end(chunk: string, cb?: Function): void;
|
||||
end(chunk: string, encoding: string, cb?: Function): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
abort(): void;
|
||||
destroy(): void;
|
||||
toJSON(): Object;
|
||||
}
|
||||
|
||||
export interface Headers {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface AuthOptions {
|
||||
user?: string;
|
||||
username?: string;
|
||||
pass?: string;
|
||||
password?: string;
|
||||
sendImmediately?: boolean;
|
||||
bearer?: string;
|
||||
}
|
||||
|
||||
export interface OAuthOptions {
|
||||
callback?: string;
|
||||
consumer_key?: string;
|
||||
consumer_secret?: string;
|
||||
token?: string;
|
||||
token_secret?: string;
|
||||
verifier?: string;
|
||||
}
|
||||
|
||||
export interface HawkOptions {
|
||||
credentials: any;
|
||||
}
|
||||
|
||||
export interface AWSOptions {
|
||||
secret: string;
|
||||
bucket?: string;
|
||||
}
|
||||
|
||||
export interface CookieJar {
|
||||
setCookie(cookie: Cookie, uri: string | url.Url, options?: any): void
|
||||
getCookieString(uri: string | url.Url): string
|
||||
getCookies(uri: string | url.Url): Cookie[]
|
||||
}
|
||||
|
||||
export interface CookieValue {
|
||||
name: string;
|
||||
value: any;
|
||||
httpOnly: boolean;
|
||||
}
|
||||
|
||||
export interface Cookie extends Array<CookieValue> {
|
||||
constructor(name: string, req: Request): void;
|
||||
str: string;
|
||||
expires: Date;
|
||||
path: string;
|
||||
toString(): string;
|
||||
}
|
||||
}
|
||||
var request: request.RequestAPI<request.Request, request.CoreOptions, request.RequiredUriUrl>;
|
||||
export = request;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Type definitions for rimraf
|
||||
// Project: https://github.com/isaacs/rimraf
|
||||
// Definitions by: Carlos Ballesteros Velasco <https://github.com/soywiz>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
// Imported from: https://github.com/soywiz/typescript-node-definitions/rimraf.d.ts
|
||||
|
||||
declare module "rimraf" {
|
||||
function rimraf(path: string, callback: (error: Error) => void): void;
|
||||
module rimraf {
|
||||
export function sync(path: string): void;
|
||||
export var EMFILE_MAX: number;
|
||||
export var BUSYTRIES_MAX: number;
|
||||
}
|
||||
export = rimraf;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
// Type definitions for should.js 3.1.2
|
||||
// Project: https://github.com/visionmedia/should.js
|
||||
// Definitions by: Alex Varju <https://github.com/varju/>, Maxime LUCE <https://github.com/SomaticIT/>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
interface Object {
|
||||
should: ShouldAssertion;
|
||||
}
|
||||
|
||||
interface ShouldAssertion {
|
||||
// basic grammar
|
||||
a: ShouldAssertion;
|
||||
an: ShouldAssertion;
|
||||
and: ShouldAssertion;
|
||||
be: ShouldAssertion;
|
||||
have: ShouldAssertion;
|
||||
with: ShouldAssertion;
|
||||
of: ShouldAssertion;
|
||||
not: ShouldAssertion;
|
||||
|
||||
// validators
|
||||
arguments: ShouldAssertion;
|
||||
empty: ShouldAssertion;
|
||||
ok: ShouldAssertion;
|
||||
true: ShouldAssertion;
|
||||
false: ShouldAssertion;
|
||||
NaN: ShouldAssertion;
|
||||
Infinity: ShouldAssertion;
|
||||
Array: ShouldAssertion;
|
||||
Object: ShouldAssertion;
|
||||
String: ShouldAssertion;
|
||||
Boolean: ShouldAssertion;
|
||||
Number: ShouldAssertion;
|
||||
Error: ShouldAssertion;
|
||||
Function: ShouldAssertion;
|
||||
eql(expected: any, description?: string): ShouldAssertion;
|
||||
equal(expected: any, description?: string): ShouldAssertion;
|
||||
within(start: number, finish: number, description?: string): ShouldAssertion;
|
||||
approximately(value: number, delta: number, description?: string): ShouldAssertion;
|
||||
type(expected: any, description?: string): ShouldAssertion;
|
||||
instanceof(constructor: Function, description?: string): ShouldAssertion;
|
||||
above(n: number, description?: string): ShouldAssertion;
|
||||
below(n: number, description?: string): ShouldAssertion;
|
||||
match(other: {}, description?: string): ShouldAssertion;
|
||||
match(other: (val: any) => any, description?: string): ShouldAssertion;
|
||||
match(regexp: RegExp, description?: string): ShouldAssertion;
|
||||
match(other: any, description?: string): ShouldAssertion;
|
||||
matchEach(other: {}, description?: string): ShouldAssertion;
|
||||
matchEach(other: (val: any) => any, description?: string): ShouldAssertion;
|
||||
matchEach(regexp: RegExp, description?: string): ShouldAssertion;
|
||||
matchEach(other: any, description?: string): ShouldAssertion;
|
||||
matchAny(other: {}, description?: string): ShouldAssertion;
|
||||
matchAny(other: (val: any) => any, description?: string): ShouldAssertion;
|
||||
matchAny(regexp: RegExp, description?: string): ShouldAssertion;
|
||||
matchAny(other: any, description?: string): ShouldAssertion;
|
||||
length(n: number, description?: string): ShouldAssertion;
|
||||
property(name: string, description?: string): ShouldAssertion;
|
||||
property(name: string, val: any, description?: string): ShouldAssertion;
|
||||
properties(names: string[]): ShouldAssertion;
|
||||
properties(name: string): ShouldAssertion;
|
||||
properties(descriptor: any): ShouldAssertion;
|
||||
properties(...properties: string[]): ShouldAssertion;
|
||||
ownProperty(name: string, description?: string): ShouldAssertion;
|
||||
contain(obj: any): ShouldAssertion;
|
||||
containEql(obj: any): ShouldAssertion;
|
||||
containDeep(obj: any): ShouldAssertion;
|
||||
containDeepOrdered(obj: any): ShouldAssertion;
|
||||
keys(...allKeys: string[]): ShouldAssertion;
|
||||
keys(allKeys: string[]): ShouldAssertion;
|
||||
header(field: string, val?: string): ShouldAssertion;
|
||||
status(code: number): ShouldAssertion;
|
||||
json: ShouldAssertion;
|
||||
html: ShouldAssertion;
|
||||
startWith(expected: string, message?: any): ShouldAssertion;
|
||||
endWith(expected: string, message?: any): ShouldAssertion;
|
||||
throw(message?: any): ShouldAssertion;
|
||||
|
||||
// deprecated
|
||||
include(obj: any, description?: string): ShouldAssertion;
|
||||
includeEql(obj: any[], description?: string): ShouldAssertion;
|
||||
|
||||
// aliases
|
||||
exactly(expected: any, description?: string): ShouldAssertion;
|
||||
instanceOf(constructor: Function, description?: string): ShouldAssertion;
|
||||
throwError(message?: any): ShouldAssertion;
|
||||
lengthOf(n: number, description?: string): ShouldAssertion;
|
||||
key(key: string): ShouldAssertion;
|
||||
haveOwnProperty(name: string, description?: string): ShouldAssertion;
|
||||
greaterThan(n: number, description?: string): ShouldAssertion;
|
||||
lessThan(n: number, description?: string): ShouldAssertion;
|
||||
}
|
||||
|
||||
interface ShouldInternal {
|
||||
// should.js's extras
|
||||
exist(actual: any, msg?: string): void;
|
||||
exists(actual: any, msg?: string): void;
|
||||
not: ShouldInternal;
|
||||
}
|
||||
|
||||
interface Internal extends ShouldInternal {
|
||||
(obj: any): ShouldAssertion;
|
||||
|
||||
// node.js's assert functions
|
||||
fail(actual: any, expected: any, message: string, operator: string): void;
|
||||
assert(value: any, message: string): void;
|
||||
ok(value: any, message?: string): void;
|
||||
equal(actual: any, expected: any, message?: string): void;
|
||||
notEqual(actual: any, expected: any, message?: string): void;
|
||||
deepEqual(actual: any, expected: any, message?: string): void;
|
||||
notDeepEqual(actual: any, expected: any, message?: string): void;
|
||||
strictEqual(actual: any, expected: any, message?: string): void;
|
||||
notStrictEqual(actual: any, expected: any, message?: string): void;
|
||||
throws(block: any, error?: any, message?: string): void;
|
||||
doesNotThrow(block: any, message?: string): void;
|
||||
ifError(value: any): void;
|
||||
inspect(value: any, obj: any): any;
|
||||
}
|
||||
|
||||
declare var should: Internal;
|
||||
declare var Should: Internal;
|
||||
interface Window {
|
||||
Should: Internal;
|
||||
}
|
||||
|
||||
declare module "should" {
|
||||
export = should;
|
||||
}
|
|
@ -3,7 +3,13 @@
|
|||
// Definitions by: William Sears <https://github.com/mrbigdog2u>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
/// <reference path="../node/node.d.ts" />
|
||||
|
||||
declare module Sinon {
|
||||
// Added definitions for Event and Document since vscode causes issues with its own typings
|
||||
interface Event {}
|
||||
interface Document {}
|
||||
|
||||
interface SinonSpyCallApi {
|
||||
// Properties
|
||||
thisValue: any;
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
declare module "tsd" {
|
||||
export class API {
|
||||
context: Context;
|
||||
constructor(context: Context);
|
||||
initConfig(overwrite: boolean): Promise<string[]>;
|
||||
readConfig(optional: boolean): Promise<void>;
|
||||
saveConfig(): Promise<string>;
|
||||
select(query: Query, options?: Options): Promise<any>;
|
||||
install(selection: any, options?: Options): Promise<InstallResult>;
|
||||
reinstall(options?: Options): Promise<InstallResult>;
|
||||
update(options?: Options, version?: string): Promise<any>;
|
||||
}
|
||||
|
||||
export class Context {
|
||||
verbose: boolean;
|
||||
paths: any;
|
||||
config: any;
|
||||
packageInfo: any;
|
||||
settings: any;
|
||||
constructor(configFile?: string, verbose?: boolean);
|
||||
stackSettings(src: string): void;
|
||||
getTypingsDir(): string;
|
||||
getInfo(details?: boolean): Object;
|
||||
}
|
||||
|
||||
interface IKeyValuePair<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
export class Hash {
|
||||
dict: IKeyValuePair<string[]>;
|
||||
}
|
||||
|
||||
export class InstallResult {
|
||||
written: Hash;
|
||||
removed: Hash;
|
||||
skipped: Hash;
|
||||
constructor(configFile?: string, verbose?: boolean);
|
||||
}
|
||||
|
||||
export class Selection {
|
||||
query: Query;
|
||||
constructor(query: Query);
|
||||
}
|
||||
|
||||
export class Options {
|
||||
minMatches: number;
|
||||
maxMatches: number;
|
||||
limitApi: number;
|
||||
resolveDependencies: boolean;
|
||||
overwriteFiles: boolean;
|
||||
saveToConfig: boolean;
|
||||
saveBundle: boolean;
|
||||
reinstallClean: boolean;
|
||||
addToBundles: string[];
|
||||
static fromJSON(json: Object): Options;
|
||||
static main: Options;
|
||||
}
|
||||
|
||||
export class Def {
|
||||
path: string;
|
||||
project: string;
|
||||
name: string;
|
||||
semver: string;
|
||||
label: string;
|
||||
isLegacy: boolean;
|
||||
isMain: boolean;
|
||||
head: any;
|
||||
history: any[];
|
||||
releases: Def[];
|
||||
constructor(path: string);
|
||||
toString(): string;
|
||||
pathTerm: string;
|
||||
nameTerm: string;
|
||||
static isDefPath(path: string): boolean;
|
||||
static getFrom(path: string): Def;
|
||||
}
|
||||
|
||||
export class VersionMatcher {
|
||||
static latest: string;
|
||||
static all: string;
|
||||
range: string;
|
||||
constructor(range?: string);
|
||||
filter(list: Def[]): Def[];
|
||||
private getLatest(list);
|
||||
}
|
||||
|
||||
export class Query {
|
||||
patterns: any[];
|
||||
versionMatcher: VersionMatcher;
|
||||
parseInfo: boolean;
|
||||
loadHistory: boolean;
|
||||
constructor(pattern?: string);
|
||||
addNamePattern(pattern: string): void;
|
||||
setVersionRange(range: string): void;
|
||||
requiresHistory: boolean;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export function getAPI(configPath: string, verbose?: boolean): API;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/// <reference path="../node_modules/vscode/typings/index.d.ts" />
|
|
@ -0,0 +1,33 @@
|
|||
# Welcome to your first VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
* This folder contains all of the files necessary for your extension
|
||||
* `package.json` - this is the manifest file in which you declare your extension and command.
|
||||
The sample plugin registers a command and defines its title and command name. With this information
|
||||
VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
|
||||
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
|
||||
The file exports one function, `activate`, which is called the very first time your extension is
|
||||
activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||
We pass the function containing the implementation of the command as the second parameter to
|
||||
`registerCommand`.
|
||||
|
||||
## Get up and running straight away
|
||||
* press `F5` to open a new window with your extension loaded
|
||||
* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
|
||||
* set breakpoints in your code inside `src/extension.ts` to debug your extension
|
||||
* find output from your extension in the debug console
|
||||
|
||||
## Make changes
|
||||
* you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`
|
||||
* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
|
||||
|
||||
## Explore the API
|
||||
* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
|
||||
|
||||
## Run tests
|
||||
* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
|
||||
* press `F5` to run the tests in a new window with your extension loaded
|
||||
* see the output of the test result in the debug console
|
||||
* make changes to `test/extension.test.ts` or create new test files inside the `test` folder
|
||||
* by convention, the test runner will only consider files matching the name pattern `**.test.ts`
|
||||
* you can create folders inside the `test` folder to structure your tests any way you want
|
Загрузка…
Ссылка в новой задаче