React search refiners renderer - An example code renderer for React Search Refiners (#742)

* Re-did the new changes to dev instead of master.

* Added templating options.

* Removed console logs

* No filter chosen should default to no filter, not two filters.

* New settings now handling locales..

* Handled the fact that IE does not support Event constructor.

* Updated documentation.

* Better images for documentation.

* Updated version history.

* Upped version number correctly.

* Update ResultsLayoutOption.ts

Removed comma

* Initial commit for react-search-refiner-renderer

* Boilerplate cleanup

* Initial readme commit.

* Updated variables image

* Updated readme.

* Fixed broken link.

* Another attempt at fixing broken link.
This commit is contained in:
taraldga 2019-01-07 09:52:43 +01:00 committed by Mikael Svenson
parent 5b74bb9352
commit e587071d01
33 changed files with 18382 additions and 49 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.7.1",
"libraryName": "react-search-refiners-renderer",
"libraryId": "41f46e12-4ce6-4826-b1e3-10e7a2a0b895",
"packageManager": "npm",
"componentType": "extension",
"extensionType": "ApplicationCustomizer"
}
}

View File

@ -0,0 +1,58 @@
# Sharepoint Framework renderer for react-search-refiners
## Summary
This sample is an example on how to use custom code renderers in the react-search-refiners project. Custom code renderers lets you render the search results from a react-search-refiners webpart in your own way, with whatever tools you prefer.
Typically, you will use SPFx Application Customizers to mount these renderers, but you may also use SPFx Webparts. This sample contains a SPFx Application Customizer which uses React and Office-UI-Fabric-React to render the search results.
<p align="center">
<img src="./img/coderenderer.gif"/>
</p>
## Used Sharepoint Framework Version
![drop](https://img.shields.io/badge/drop-1.7.1-green.svg)
## Applies to
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
* [React Search Refiners](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-search-refiners)
## Solution
Solution|Author(s)
--------|---------
react-search-refiners-renderers | Tarald Gåsbakk - [@taraldga](http://www.twitter.com/taraldgasbakk)
## Version history
Version|Date|Comments
-------|----|--------
1.0 | Jan 06, 2019 | Initial release
## Minimal Path to Awesome
- Follow the Minimal Path to Awesome for [React Search Refiners](https://github.com/SharePoint/sp-dev-fx-webparts/tree/master/samples/react-search-refiners).
- In this repository, run from the command line:
- `npm install`
- `gulp serve`
## Creating your own renderers
The renderer bundlet in this sample is a simple renderer designed to show how a simple implementation of a code renderer would look like. Following is a quick tour of the setup-process and the different settings you will encounter when creating your own renderer.
### Registering a new renderer
The renderers work by announcing themselves to any react-search-refiners webparts that exists on the pages they are installed on. The registration process is handled by the 'registerRenderer'- function in the 'ResultService' - class in './src/services'. A typicall registration will take place onInit.
The parameters of the registerRenderer function:
- **rendererId**: The id of the current renderer. Used to idententify which renderer the user has selected. Typically 'this.componentId'.
- **rendererName**: The displayName of the renderer in the react-search-refiners webpart property panel.
- **rendererIcon**: The office-ui-fabric-react icon that should be displayed in the property pane of the webpart.
- **callback**: The callback function that is used to render the webpart. This will be called when the renderer is selected and new data is available.
- **customFields**: Optional names of custom fields that may be used for templating in the renderer.
<p align="center"><img width="300px" src="./img/renderervariables.png"/><p>
### Templating
By registering a field-name in the 'customFields' value, you may expose them to the user, letting them choose which fields should be displayed in different places in your renderer. In the bundled example, a subheader is passed along as a template field. The dropdown selection that the user can chose elements from are the elements selected for fetching by the search webpart.
<p align="center">
<img src="./img/coderenderertemplate.gif"/>
</p>

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"code-renderer-application-customizer": {
"components": [
{
"entrypoint": "./lib/extensions/codeRenderer/CodeRendererApplicationCustomizer.js",
"manifest": "./src/extensions/codeRenderer/CodeRendererApplicationCustomizer.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"CodeRendererApplicationCustomizerStrings": "lib/extensions/codeRenderer/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-search-refiners-renderer",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,26 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": " Code renderer for React Search Refiners",
"id": "41f46e12-4ce6-4826-b1e3-10e7a2a0b895",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"features": [
{
"title": "Application Extension - Deployment of custom action.",
"description": "Deploys a custom action with ClientSideComponentId association",
"id": "cbc3a036-8112-4a19-a924-4bfe45af2675",
"version": "1.0.0.0",
"assets": {
"elementManifests": [
"elements.xml",
"clientsideinstance.xml"
]
}
}
]
},
"paths": {
"zippedPackage": "solution/react-search-refiners-renderer.sppkg"
}
}

View File

@ -0,0 +1,29 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"serveConfigurations": {
"default": {
"pageUrl": "https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx",
"customActions": {
"6c5daeed-02bf-4f02-806c-7e44a770d71c": {
"location": "ClientSideExtension.ApplicationCustomizer",
"properties": {
"testMessage": "Test message"
}
}
}
},
"codeRenderer": {
"pageUrl": "https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx",
"customActions": {
"6c5daeed-02bf-4f02-806c-7e44a770d71c": {
"location": "ClientSideExtension.ApplicationCustomizer",
"properties": {
"testMessage": "Test message"
}
}
}
}
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,7 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
{
"name": "react-search-refiners-renderer",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/decorators": "1.7.1",
"@microsoft/sp-application-base": "1.7.1",
"@microsoft/sp-core-library": "1.7.1",
"@microsoft/sp-dialog": "1.7.1",
"@types/es6-promise": "0.0.33",
"@types/webpack-env": "1.13.1",
"custom-event-polyfill": "^1.0.6",
"moment": "^2.23.0",
"office-ui-fabric-react": "^5.133.0",
"react": "16.3.2",
"react-dom": "16.3.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1",
"@microsoft/sp-tslint-rules": "1.7.1",
"@microsoft/sp-module-interfaces": "1.7.1",
"@microsoft/sp-webpart-workbench": "1.7.1",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ClientSideComponentInstance
Title="CodeRenderer"
Location="ClientSideExtension.ApplicationCustomizer"
ComponentId="6c5daeed-02bf-4f02-806c-7e44a770d71c"
Properties="{&quot;testMessage&quot;:&quot;Test message&quot;}">
</ClientSideComponentInstance>
</Elements>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Title="CodeRenderer"
Location="ClientSideExtension.ApplicationCustomizer"
ClientSideComponentId="6c5daeed-02bf-4f02-806c-7e44a770d71c"
ClientSideComponentProperties="{&quot;testMessage&quot;:&quot;Test message&quot;}">
</CustomAction>
</Elements>

View File

@ -0,0 +1,13 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.json",
"id": "6c5daeed-02bf-4f02-806c-7e44a770d71c",
"alias": "PnP Search Code-Renderer",
"componentType": "Extension",
"extensionType": "ApplicationCustomizer",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false
}

View File

@ -0,0 +1,39 @@
import { override } from '@microsoft/decorators';
import {
BaseApplicationCustomizer
} from '@microsoft/sp-application-base';
import { ResultService, ISearchEvent } from '../../services/ResultService/ResultService';
import IResultService from '../../services/ResultService/IResultService';
import SearchResult from './SearchResult/SearchResults';
import * as ReactDOM from 'react-dom';
import * as React from 'react';
export interface ISearchRendererApplicationCustomizerProperties {
}
export default class SearchRendererApplicationCustomizer
extends BaseApplicationCustomizer<ISearchRendererApplicationCustomizerProperties> {
private _resultService: IResultService;
@override
public onInit(): Promise<void> {
this._resultService = new ResultService();
this.onChangeHappened.bind(this);
this._resultService.registerRenderer(this.componentId, 'CodeRenderer', 'QueryList', this.onChangeHappened, ['Subheader']);
return Promise.resolve();
}
public onChangeHappened(e: ISearchEvent) {
const subheaderFieldName = e.customTemplateFieldValues[0].searchProperty && e.customTemplateFieldValues[0].searchProperty.length > 0 ? e.customTemplateFieldValues[0].searchProperty : 'Path';
const resultDisplay = React.createElement(SearchResult, {
searchResults: e.results,
componentId: e.rendererId,
subheaderFieldName: subheaderFieldName,
});
let node = document.getElementById(e.mountNode);
if (node) {
ReactDOM.render(resultDisplay, node);
}
}
}

View File

@ -0,0 +1,8 @@
import { ISearchResults } from '../../../models/ISearchResult';
import IResultService from '../../../services/ResultService/IResultService';
export default interface ISearchResultProps {
searchResults: ISearchResults;
componentId: string;
subheaderFieldName: string;
}

View File

@ -0,0 +1,86 @@
.resultContainer {
li.resultItem {
display: block;
a {
transition: box-shadow 0.467s ease;
text-decoration: none;
display: flex;
padding: 15px 0 0 0;
border-top: 1px solid #eaeaea;
&:focus {
box-shadow: 0 0 0 3px #FFBD65;
outline: none;
}
&:hover {
box-shadow: 0 8px 10px -2px rgba(0,0,0,.1);
}
&:before {
display: block;
content: " ";
border-bottom: 1px solid #eaeaea;
}
.imageContainer {
height:100px;
width: 78px;
display: flex;
justify-content: center;
flex-direction: column;
>img {
max-width: 78px;
max-height: 48px;
margin: auto 0;
width: auto;
height: auto;
}
}
.coin {
width: 48px;
height: 100px;
display: flex;
justify-content: center;
flex-direction: column;
padding: 0 15px;
> div > div {
border-radius: 0;
}
}
> article {
margin-left: 10px;
h3, p, ul {
font-weight: 400;
font-family:"Segoe UI Web (West European)",Segoe UI,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,sans-serif;
}
p, ul {
font-size: 12px;
color: #666666;
}
h3 {
color: #106ebe;
text-decoration: none;
margin: 0;
font-size: 17px;
}
ul {
list-style: none;
padding: 0;
li {
display: inline-block;
}
li:last-child {
&:before {
content: "\2022";
display: inline-block;
margin: 0 .5em;
}
}
}
}
}
}
}

View File

@ -0,0 +1,42 @@
import * as React from 'react';
import ISearchResultProps from './ISearchResultProps';
import styles from './SearchResult.module.scss';
import { PersonaCoin } from 'office-ui-fabric-react/lib/PersonaCoin';
import * as moment from 'moment';
export default class SearchResult extends React.Component<ISearchResultProps, {}> {
public render() {
return (
<div className="template_root">
<ul className={styles.resultContainer}>
{this.props.searchResults.RelevantResults.map(result => {
const image = result.SiteLogo ?
<div className={styles.imageContainer}><img src={result.SiteLogo}></img></div> :
<PersonaCoin className={styles.coin} text={result.Title} />;
return (
<li className={styles.resultItem}>
<a href={result.Path}>
{image}
<article>
<header>
<h3>{result.Title}</h3>
<p>{result[this.props.subheaderFieldName]}</p>
<ul>
<li>Oppdatert for {result.Updated ? this.fmtDateString(result.Updated) : this.fmtDateString(result.Created)}</li>
<li>{result.CreatedBy}</li>
</ul>
</header>
</article>
</a>
</li>
);
})}
</ul>
</div>
);
}
private fmtDateString(utcString) {
return moment(utcString).fromNow();
}
}

View File

@ -0,0 +1,4 @@
define([], function() {
return {
}
});

View File

@ -0,0 +1,7 @@
declare interface ICodeRendererApplicationCustomizerStrings {
}
declare module 'CodeRendererApplicationCustomizerStrings' {
const strings: ICodeRendererApplicationCustomizerStrings;
export = strings;
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,34 @@
export interface ISearchResults {
RelevantResults: ISearchResult[];
RefinementResults: IRefinementResult[];
PromotedResults?: IPromotedResult[];
TotalRows?: number;
}
export interface ISearchResult {
[key: string]: string;
IconSrc?: string;
}
export interface IRefinementResult {
FilterName: string;
Values: IRefinementValue[];
}
export interface IPromotedResult {
Url: string;
Title: string;
Description: string;
}
export interface IRefinementValue {
RefinementCount: number;
RefinementName: string;
RefinementToken: string;
RefinementValue: string;
}
export interface IRefinementFilter {
FilterName: string;
Value: IRefinementValue;
}

View File

@ -0,0 +1,27 @@
import { ISearchResults } from "../../models/ISearchResult";
import { IRenderer } from "./ResultService";
import {ICustomTemplateFieldValue} from './ResultService';
export default interface IResultService {
/**
* Persists the results to the local storage and fires and update event.
* @param results The new results
* @param rendererId The Id of the custom action chosen to render the resultdata.
* @param mountNode The name of the html node which the renderers should use to display the results
*/
updateResultData(results: ISearchResults, rendererId: string, mountNode: string, customTemplateFieldValues?: ICustomTemplateFieldValue[]);
/**
* Registerer the renderer as an renderer to be picked up by the search-refiners webpart.
* @param rendererId The id of the renderer
* @param rendererName The name that should be displayed in the search-refiners webpart
* @param rendererIcon The office-ui-fabric icon to be displayed.
* @param callback The function that should run whenever the renderer recieves data
*/
registerRenderer(rendererId: string, rendererName: string, rendererIcon: string, callback: (e) => void, customFields?: string[]);
/**
* Get all registered renderers on the current page.
*/
getRegisteredRenderers(): IRenderer[];
}

View File

@ -0,0 +1,61 @@
import { ISearchResults } from "../../models/ISearchResult";
import IResultService from "./IResultService";
import 'custom-event-polyfill';
export interface ISearchEvent extends CustomEvent {
rendererId?: string;
results?: ISearchResults;
mountNode?: string;
customTemplateFieldValues?: ICustomTemplateFieldValue[];
}
export interface IRenderer {
id: string;
name: string;
icon: string;
customFields?: string[];
}
export interface ICustomTemplateFieldValue {
fieldName: string;
searchProperty: string;
}
export class ResultService implements IResultService {
private SEARCH_CHANGED_EVENT_NAME: string = "pnp-spfx-search-changed";
private SEARCH_RENDERERS_OBJECT_NAME: string = "pnp-spfx-search-renderers";
public updateResultData(results: ISearchResults, rendererId: string, mountNode: string, customTemplateFieldValues?: ICustomTemplateFieldValue[]) {
let searchEvent: ISearchEvent = new CustomEvent(this.SEARCH_CHANGED_EVENT_NAME);
searchEvent.rendererId = rendererId;
searchEvent.results = results;
searchEvent.mountNode = mountNode;
searchEvent.customTemplateFieldValues = customTemplateFieldValues;
window.dispatchEvent(searchEvent);
}
public registerRenderer(rendererId: string, rendererName: string, rendererIcon: string, callback: (e: ISearchEvent) => void, customFields?: string[]): void {
const newRenderer = {
id: rendererId,
name: rendererName,
icon: rendererIcon,
customFields: customFields
};
if( window[this.SEARCH_RENDERERS_OBJECT_NAME] === undefined) {
window[this.SEARCH_RENDERERS_OBJECT_NAME] = [newRenderer];
} else {
window[this.SEARCH_RENDERERS_OBJECT_NAME].push(newRenderer);
}
addEventListener(this.SEARCH_CHANGED_EVENT_NAME, (e: ISearchEvent) => this.handleNewDataRegistered(e, rendererId, callback));
}
public getRegisteredRenderers(): IRenderer[] {
return window[this.SEARCH_RENDERERS_OBJECT_NAME];
}
private handleNewDataRegistered(e: ISearchEvent, rendererId, callback: (e) => void ) {
if(e.rendererId === rendererId) {
callback(e);
}
}
}

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}

View File

@ -246,34 +246,7 @@ Handlebars [partials](https://handlebarsjs.com/partials.html) are used behind th
#### Custom code renderers
You may also define your own renderers, which most often should be SPFx application customizers. These should use the resultservice to register themselves as renderers, and will upon registration be available as a rendering choice in the "Result Layouts" Section.
<p align="center">
<img width="500px" src="./images/results_layout.PNG"/>
</p>
When registering new renderers, must include:
- The component id
- The name of the renderer (displayed in the settings panel of the react-search-refiners webpart)
- The icon of the renderer (displayed in the settings panel of the react-search-refiners webpart)
- The Update-function that takes care of any updates in the results.
- A list of properties that may be selected for usage of template funcitonality (a list of strings that should be descriptions of the template fields).
A typical registration looks like this:
```
this._resultService = new ResultService();
this.onChangeHappened.bind(this);
this._resultService.registerRenderer(this.componentId, 'Defaultrenderer', 'QueryList', this.onChangeHappened, ['subheader']);
```
#### Custom code renderer template fields
If you register fields as template fields in your renderer, they will become editable through a menu in the webpart.
The users may choose what values to display from a dropdown, which is populated by properties chosen in the "Selected properties" field in the webpart settings.
<p align="center">
<img width="5000px" src="./images/custom_template_fields_selection.PNG"/>
</p>
More information about custom code renderers may be found in a [seperate sample](../react-search-refiners-renderer), which showcases such a renderer.
#### Query variables

View File

@ -1,6 +1,6 @@
{
"name": "pnp-react-search-refiners",
"version": "2.3.0",
"version": "2.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -5092,6 +5092,11 @@
"integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
"dev": true
},
"custom-event-polyfill": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.6.tgz",
"integrity": "sha512-3FxpFlzGcHrDykwWu+xWVXZ8PfykM/9/bI3zXb953sh+AjInZWcQmrnmvPoZgiqNjmbtTm10PWvYqvRW527x6g=="
},
"d": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
@ -7137,7 +7142,7 @@
"dev": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
"safer-buffer": "2.1.2"
}
},
"ignore-walk": {
@ -7175,7 +7180,7 @@
"bundled": true,
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
"number-is-nan": "1.0.1"
}
},
"isarray": {
@ -7235,9 +7240,9 @@
"dev": true,
"optional": true,
"requires": {
"debug": "^2.1.2",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
"debug": "2.6.9",
"iconv-lite": "0.4.21",
"sax": "1.2.4"
}
},
"node-pre-gyp": {
@ -7264,8 +7269,8 @@
"dev": true,
"optional": true,
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
"abbrev": "1.1.1",
"osenv": "0.1.5"
}
},
"npm-bundled": {
@ -7280,8 +7285,8 @@
"dev": true,
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
"ignore-walk": "3.0.1",
"npm-bundled": "1.0.3"
}
},
"npmlog": {
@ -7333,8 +7338,8 @@
"dev": true,
"optional": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
"os-homedir": "1.0.2",
"os-tmpdir": "1.0.2"
}
},
"path-is-absolute": {
@ -7355,10 +7360,10 @@
"dev": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
"deep-extend": "0.5.1",
"ini": "1.3.5",
"minimist": "1.2.0",
"strip-json-comments": "2.0.1"
},
"dependencies": {
"minimist": {
@ -7433,9 +7438,9 @@
"bundled": true,
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
"code-point-at": "1.1.0",
"is-fullwidth-code-point": "1.0.0",
"strip-ansi": "3.0.1"
}
},
"string_decoder": {
@ -7444,7 +7449,7 @@
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
"safe-buffer": "5.1.1"
}
},
"strip-ansi": {
@ -7452,7 +7457,7 @@
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
"ansi-regex": "2.1.1"
}
},
"strip-json-comments": {