properties-file 1.0.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +215 -0
- package/lib/content/index.d.ts +18 -0
- package/lib/content/index.js +61 -0
- package/lib/file/index.d.ts +21 -0
- package/lib/file/index.js +32 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.js +13 -0
- package/lib/loader/webpack.d.ts +10 -0
- package/lib/loader/webpack.js +14 -0
- package/lib/properties-file.d.ts +7 -0
- package/lib/properties.d.ts +55 -0
- package/lib/properties.js +91 -0
- package/lib/property-line.d.ts +22 -0
- package/lib/property-line.js +48 -0
- package/lib/property.d.ts +64 -0
- package/lib/property.js +198 -0
- package/package.json +70 -29
- package/.npmignore +0 -4
- package/index.js +0 -25
- package/readme.md +0 -40
- package/test/stringify_result.properties +0 -72
- package/test/test.js +0 -23
- package/test/test.properties +0 -72
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Avansai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# properties-file
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.npmjs.com/package/properties-file)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
[](https://snyk.io/test/github/Avansai/properties-file?targetFile=package.json)
|
|
8
|
+
|
|
9
|
+
.properties file parser, JSON converter and Webpack loader.
|
|
10
|
+
|
|
11
|
+
## Installation 💻
|
|
12
|
+
|
|
13
|
+
> ⚠ in June 2022 we have released version 2 of this package which is not compatible with the previous versions. Make sure to read the documentation before upgrading.
|
|
14
|
+
|
|
15
|
+
Add the package as a dependency:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm install properties-file
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## What's in it for me? 🤔
|
|
22
|
+
|
|
23
|
+
- A modern TypeScript library that reproduces exactly the [Properties Java implementation](/assets/java-implementation.md).
|
|
24
|
+
- Flexible APIs:
|
|
25
|
+
- `propertiesToJson` allows quick conversion from `.properties` files to JSON.
|
|
26
|
+
- `getProperties` returns a `Properties` object that allows insights into parsing issues such as key collisions.
|
|
27
|
+
- `propertiesToJson` & `getProperties` also have a browser-compatible version when passing directly the content of a file using the APIs under `properties-file/content`.
|
|
28
|
+
- Out of the box Webpack loader to `import` `.properties` files directly in your application.
|
|
29
|
+
- 100% test coverage based on the output from a Java implementation.
|
|
30
|
+
- Active maintenance (many popular .properties packages have been inactive years).
|
|
31
|
+
|
|
32
|
+
## Usage 🎬
|
|
33
|
+
|
|
34
|
+
We put a lot of effort into adding [TSDoc](https://tsdoc.org/) to all our APIs. Please check directly in your IDE if you are unsure how to use certain APIs provided in our examples.
|
|
35
|
+
|
|
36
|
+
Both APIs (`getProperties` and `propertiesToJson`) directly under `properties-file` depend on [`fs`](https://nodejs.org/api/fs.html) which means they cannot be used by browsers. If you cannot use `fs` and already have a `.properties` file content, the same APIs are available under `properties-file/content`. Instead of taking the `filePath` as the first argument, they take `content`. The example below will use "`fs`" APIs since they are the most common use cases.
|
|
37
|
+
|
|
38
|
+
### `propertiesToJson`
|
|
39
|
+
|
|
40
|
+
This API is probably the most used. You have a `.properties` file that you want to open and access like a simple key/value JSON object. Here is how this can be done with a single API call:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { propertiesToJson } from 'properties-file';
|
|
44
|
+
|
|
45
|
+
console.log(propertiesToJson('hello-world.properties'));
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Output:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
{ hello: 'hello', world: 'world' }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you cannot use `fs` and already have the content of a `.properties` file, your code would look like this instead:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { propertiesToJson } from 'properties-file/content';
|
|
58
|
+
|
|
59
|
+
// ...some code to get the .properties file content into a variable called `propertiesFileContent`
|
|
60
|
+
|
|
61
|
+
console.log(propertiesToJson(propertiesFileContent));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `getProperties` (advanced use case)
|
|
65
|
+
|
|
66
|
+
Java's implementation of `Properties` is quite resilient. In fact, there are only two ways an exception can be thrown:
|
|
67
|
+
|
|
68
|
+
- The file is not found.
|
|
69
|
+
- A (`\u`) Unicode escape character is malformed.
|
|
70
|
+
|
|
71
|
+
This means that almost all files will be valid.
|
|
72
|
+
|
|
73
|
+
But what about a file that has duplicate keys? Duplicate keys have no reason to exist and they probably should have thrown errors as well but instead Java decided to simply overwrite the value with the latest occurrence in a file.
|
|
74
|
+
|
|
75
|
+
So how can we know if there were duplicate keys if we want to log some warnings? Simply by using `getProperties` which will return all the data that was used to parse the content. Here is an example on how it can be used:
|
|
76
|
+
|
|
77
|
+
```properties
|
|
78
|
+
# collisions-test.properties
|
|
79
|
+
hello: hello1
|
|
80
|
+
world: world1
|
|
81
|
+
world: world2
|
|
82
|
+
hello: hello2
|
|
83
|
+
world: world3
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { getProperties } from 'properties-file';
|
|
88
|
+
|
|
89
|
+
const properties = getProperties('assets/tests/collisions-test.properties');
|
|
90
|
+
|
|
91
|
+
properties.collection.forEach((property) => {
|
|
92
|
+
console.log(`${property.key} => '${property.value}'`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Outputs:
|
|
97
|
+
*
|
|
98
|
+
* hello => 'hello2'
|
|
99
|
+
* world => 'world3'
|
|
100
|
+
*
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
const keyCollisions = properties.getKeyCollisions();
|
|
104
|
+
|
|
105
|
+
keyCollisions.forEach((keyCollision) => {
|
|
106
|
+
console.warn(
|
|
107
|
+
`Found a key collision for key '${
|
|
108
|
+
keyCollision.key
|
|
109
|
+
}' on lines ${keyCollision.startingLineNumbers.join(
|
|
110
|
+
', '
|
|
111
|
+
)} (will use the value at line ${keyCollision.getApplicableLineNumber()}).`
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Outputs:
|
|
117
|
+
*
|
|
118
|
+
* Found a key collision for key 'hello' on lines 1, 4 (will use the value at line 4).
|
|
119
|
+
* Found a key collision for key 'world' on lines 2, 3, 5 (will use the value at line 5).
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Webpack File Loader
|
|
125
|
+
|
|
126
|
+
If you would like to import `.properties` directly using `import`, this package comes with its own Webpack file loader located under `properties-file/webpack-loader`. Here is an example of how to configure it:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
// webpack.config.js
|
|
130
|
+
module.exports = {
|
|
131
|
+
module: {
|
|
132
|
+
rules: [
|
|
133
|
+
{
|
|
134
|
+
test: /\.properties$/i,
|
|
135
|
+
use: [
|
|
136
|
+
{
|
|
137
|
+
loader: 'properties-file/webpack-loader',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
As soon as you configure Webpack, the `.properties` type should be available in your IDE when using `import`. If you ever need to add it manually, you can add a `*.properties` type declaration file at the root of your application, like this:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
// properties-file.d.ts
|
|
150
|
+
declare module '*.properties' {
|
|
151
|
+
const properties: { readonly [key: string]: string };
|
|
152
|
+
export default properties;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
By adding these configurations you should now be able to import directly `.properties` files just like this:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import helloWorld from './hello-world.properties';
|
|
160
|
+
|
|
161
|
+
console.dir(helloWorld);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Output:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{ "hello": "world" }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Why another `.properties` file package?
|
|
171
|
+
|
|
172
|
+
There are probably over 20 similar packages available but:
|
|
173
|
+
|
|
174
|
+
- A lot of the most popular packages have had no activity for over 5 years.
|
|
175
|
+
- A large portion of the packages will not replicate the current Java implementation.
|
|
176
|
+
- No package offers the same capabilities as this one.
|
|
177
|
+
|
|
178
|
+
Unfortunately the `.properties` file specification is not well documented. One reason for this is that it was originally used in Java to store configurations. Most applications will handle this using JSON, YAML or other modern formats today because the formats are more flexible.
|
|
179
|
+
|
|
180
|
+
### So why `.properties` files?
|
|
181
|
+
|
|
182
|
+
While many options exists today to handle configurations, `.properties` file remain one of the best option to store localizable strings (also known as messages). On the Java side, `PropertyResourceBundle` is how most implementations handle localization today. Because of its simplicity and maturity, `.properties` files remain one of the best options today when it comes to internationalization (i18n):
|
|
183
|
+
|
|
184
|
+
| File format | Key/value based | Supports inline comments | Built for localization | Good linguistic tools support |
|
|
185
|
+
| ------------- | ---------------- | ------------------------ | ---------------------- | ----------------------------- |
|
|
186
|
+
| `.properties` | Yes | Yes | Yes (Resource Bundles) | Yes |
|
|
187
|
+
| `JSON` | No (can do more) | No (requires JSON5) | No | Depends on the schema |
|
|
188
|
+
| `YAML` | No (can do more) | Yes | No | Depends on the schema |
|
|
189
|
+
|
|
190
|
+
By having good JavaScript/TypeScript support for `.properties` files, it provides more options when it comes to i18n.
|
|
191
|
+
|
|
192
|
+
### How does this package work?
|
|
193
|
+
|
|
194
|
+
Basically our goal was to offer parity with the Java implementation, which is the closest thing to a specification `.properties` file have. Here is in a nutshell the logic behind this package:
|
|
195
|
+
|
|
196
|
+
1. Split the file content by lines (create line objects)
|
|
197
|
+
2. Create `LineObjects` by combining multi-line properties and removing trailing backslash
|
|
198
|
+
3. Create `PropertyObjects` from `LineObjects` that combined all lines of a property
|
|
199
|
+
4. Identify the key/value delimiter and populate escaped keys and values.
|
|
200
|
+
5. Unescape keys and values
|
|
201
|
+
6. Create a `PropertiesObject` that will include all `PropertyObjects` while removing collisions
|
|
202
|
+
|
|
203
|
+
Just like Java, if a Unicode escaped characters (`\u`) is malformed, it will throw an error. But of course, we do not recommend using Unicode escaped characters but rather UTF-8 encoding that supports more characters.
|
|
204
|
+
|
|
205
|
+
## Additional references
|
|
206
|
+
|
|
207
|
+
- Java [Test Sandbox](https://codehs.com/sandbox/id/java-main-kYynuh?filename=TestProperties.java)
|
|
208
|
+
- Java's `Properties` class [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/Properties.html)
|
|
209
|
+
- Java's `PropertyResourceBundle` [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/PropertyResourceBundle.html)
|
|
210
|
+
- Java's Internationalization [Guide](https://docs.oracle.com/en/java/javase/18/intl/internationalization-overview.html)
|
|
211
|
+
- Wikipedia's .properties [page](https://en.wikipedia.org/wiki/.properties)
|
|
212
|
+
|
|
213
|
+
### Special mention
|
|
214
|
+
|
|
215
|
+
Thanks to [@calibr](https://github.com/calibr), the creator of [properties-file version 1.0](https://github.com/calibr/properties-file), for letting us use the [https://www.npmjs.com/package/properties-file](https://www.npmjs.com/package/properties-file) package name. We hope that it will make it easier to find our package.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { KeyValueObject } from '../';
|
|
2
|
+
import { Properties } from '../properties';
|
|
3
|
+
/**
|
|
4
|
+
* Get a `Properties` object from the content of a `.properties` file.
|
|
5
|
+
*
|
|
6
|
+
* @param content - the content of a `.properties` file.
|
|
7
|
+
*
|
|
8
|
+
* @returns A `Properties` object representing the content of a `.properties` file.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getProperties(content: string): Properties;
|
|
11
|
+
/**
|
|
12
|
+
* Converts the content of a `.properties` file to JSON.
|
|
13
|
+
*
|
|
14
|
+
* @param content - the content of a `.properties` file.
|
|
15
|
+
*
|
|
16
|
+
* @returns A (JSON) key/value object representing the content of a `.properties` file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function propertiesToJson(content: string): KeyValueObject;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.propertiesToJson = exports.getProperties = void 0;
|
|
4
|
+
var properties_1 = require("../properties");
|
|
5
|
+
var property_1 = require("../property");
|
|
6
|
+
var property_line_1 = require("../property-line");
|
|
7
|
+
/**
|
|
8
|
+
* Get a `Properties` object from the content of a `.properties` file.
|
|
9
|
+
*
|
|
10
|
+
* @param content - the content of a `.properties` file.
|
|
11
|
+
*
|
|
12
|
+
* @returns A `Properties` object representing the content of a `.properties` file.
|
|
13
|
+
*/
|
|
14
|
+
function getProperties(content) {
|
|
15
|
+
// Remove BOM character if present and create an array from lines.
|
|
16
|
+
var lines = (content.charCodeAt(0) === 0xfeff ? content.slice(1) : content).split(/\r?\n/);
|
|
17
|
+
/** Line number while parsing properties file content. */
|
|
18
|
+
var lineNumber = 0;
|
|
19
|
+
/** The current property object being parsed. */
|
|
20
|
+
var property;
|
|
21
|
+
/** The collection of property objects. */
|
|
22
|
+
var properties = new properties_1.Properties();
|
|
23
|
+
for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
|
|
24
|
+
var line = lines_1[_i];
|
|
25
|
+
lineNumber++;
|
|
26
|
+
var propertyLine = new property_line_1.PropertyLine(line, !!property);
|
|
27
|
+
if (!property) {
|
|
28
|
+
// Check if the line is a new property.
|
|
29
|
+
if (propertyLine.isComment || propertyLine.isBlank) {
|
|
30
|
+
continue; // Skip line if its a comment or blank.
|
|
31
|
+
}
|
|
32
|
+
// The line is a new property.
|
|
33
|
+
property = new property_1.Property(propertyLine, lineNumber);
|
|
34
|
+
if (propertyLine.continues) {
|
|
35
|
+
continue; // Continue parsing the next line.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Continue parsing an existing property.
|
|
40
|
+
property.addLine(propertyLine);
|
|
41
|
+
if (propertyLine.continues) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// If the line does not continue, add the property to the collection.
|
|
46
|
+
property = properties.add(property);
|
|
47
|
+
}
|
|
48
|
+
return properties;
|
|
49
|
+
}
|
|
50
|
+
exports.getProperties = getProperties;
|
|
51
|
+
/**
|
|
52
|
+
* Converts the content of a `.properties` file to JSON.
|
|
53
|
+
*
|
|
54
|
+
* @param content - the content of a `.properties` file.
|
|
55
|
+
*
|
|
56
|
+
* @returns A (JSON) key/value object representing the content of a `.properties` file.
|
|
57
|
+
*/
|
|
58
|
+
function propertiesToJson(content) {
|
|
59
|
+
return getProperties(content).toJson();
|
|
60
|
+
}
|
|
61
|
+
exports.propertiesToJson = propertiesToJson;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { KeyValueObject, Properties } from '../';
|
|
3
|
+
export { KeyValueObject } from '../';
|
|
4
|
+
/**
|
|
5
|
+
* Get a `Properties` object from the content of a `.properties` file.
|
|
6
|
+
*
|
|
7
|
+
* @param filePath - The file path of the `.properties` file.
|
|
8
|
+
* @param encoding - The encoding of the file to parse (default is UTF-8).
|
|
9
|
+
*
|
|
10
|
+
* @returns A `Properties` object representing the content of a `.properties` file.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getProperties(filePath: string, encoding?: BufferEncoding): Properties;
|
|
13
|
+
/**
|
|
14
|
+
* Converts the content of a `.properties` file to JSON.
|
|
15
|
+
*
|
|
16
|
+
* @param filePath - The file path of the `.properties` file.
|
|
17
|
+
* @param encoding - The encoding of the file to parse (default is UTF-8).
|
|
18
|
+
*
|
|
19
|
+
* @returns A (JSON) key/value object representing the content of a `.properties` file.
|
|
20
|
+
*/
|
|
21
|
+
export declare function propertiesToJson(filePath: string, encoding?: BufferEncoding): KeyValueObject;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.propertiesToJson = exports.getProperties = void 0;
|
|
4
|
+
var fs_1 = require("fs");
|
|
5
|
+
var content_1 = require("../content/");
|
|
6
|
+
/**
|
|
7
|
+
* Get a `Properties` object from the content of a `.properties` file.
|
|
8
|
+
*
|
|
9
|
+
* @param filePath - The file path of the `.properties` file.
|
|
10
|
+
* @param encoding - The encoding of the file to parse (default is UTF-8).
|
|
11
|
+
*
|
|
12
|
+
* @returns A `Properties` object representing the content of a `.properties` file.
|
|
13
|
+
*/
|
|
14
|
+
function getProperties(filePath, encoding) {
|
|
15
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
16
|
+
throw Error("file not found at ".concat(filePath));
|
|
17
|
+
}
|
|
18
|
+
return (0, content_1.getProperties)((0, fs_1.readFileSync)(filePath, encoding ? encoding : 'utf-8'));
|
|
19
|
+
}
|
|
20
|
+
exports.getProperties = getProperties;
|
|
21
|
+
/**
|
|
22
|
+
* Converts the content of a `.properties` file to JSON.
|
|
23
|
+
*
|
|
24
|
+
* @param filePath - The file path of the `.properties` file.
|
|
25
|
+
* @param encoding - The encoding of the file to parse (default is UTF-8).
|
|
26
|
+
*
|
|
27
|
+
* @returns A (JSON) key/value object representing the content of a `.properties` file.
|
|
28
|
+
*/
|
|
29
|
+
function propertiesToJson(filePath, encoding) {
|
|
30
|
+
return getProperties(filePath, encoding).toJson();
|
|
31
|
+
}
|
|
32
|
+
exports.propertiesToJson = propertiesToJson;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="./properties-file" />
|
|
2
|
+
|
|
3
|
+
export { Properties, KeyLineNumbers } from './properties';
|
|
4
|
+
export { Property } from './property';
|
|
5
|
+
export { PropertyLine } from './property-line';
|
|
6
|
+
export { getProperties } from './file';
|
|
7
|
+
export { propertiesToJson } from './file';
|
|
8
|
+
/**
|
|
9
|
+
* A simple "key/value" object.
|
|
10
|
+
*/
|
|
11
|
+
export declare type KeyValueObject = {
|
|
12
|
+
[key: string]: string;
|
|
13
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.propertiesToJson = exports.getProperties = exports.PropertyLine = exports.Property = exports.Properties = void 0;
|
|
4
|
+
var properties_1 = require("./properties");
|
|
5
|
+
Object.defineProperty(exports, "Properties", { enumerable: true, get: function () { return properties_1.Properties; } });
|
|
6
|
+
var property_1 = require("./property");
|
|
7
|
+
Object.defineProperty(exports, "Property", { enumerable: true, get: function () { return property_1.Property; } });
|
|
8
|
+
var property_line_1 = require("./property-line");
|
|
9
|
+
Object.defineProperty(exports, "PropertyLine", { enumerable: true, get: function () { return property_line_1.PropertyLine; } });
|
|
10
|
+
var file_1 = require("./file");
|
|
11
|
+
Object.defineProperty(exports, "getProperties", { enumerable: true, get: function () { return file_1.getProperties; } });
|
|
12
|
+
var file_2 = require("./file");
|
|
13
|
+
Object.defineProperty(exports, "propertiesToJson", { enumerable: true, get: function () { return file_2.propertiesToJson; } });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="../properties-file" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webpack file loader for `.properties` files.
|
|
5
|
+
*
|
|
6
|
+
* @param content - the content of a `.properties` file.
|
|
7
|
+
*
|
|
8
|
+
* @returns A Webpack file loader string containing the content of a `.properties` file.
|
|
9
|
+
*/
|
|
10
|
+
export default function (content: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var content_1 = require("../content");
|
|
4
|
+
/**
|
|
5
|
+
* Webpack file loader for `.properties` files.
|
|
6
|
+
*
|
|
7
|
+
* @param content - the content of a `.properties` file.
|
|
8
|
+
*
|
|
9
|
+
* @returns A Webpack file loader string containing the content of a `.properties` file.
|
|
10
|
+
*/
|
|
11
|
+
function default_1(content) {
|
|
12
|
+
return "module.exports = ".concat(JSON.stringify((0, content_1.propertiesToJson)(content)), ";");
|
|
13
|
+
}
|
|
14
|
+
exports.default = default_1;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { KeyValueObject } from './';
|
|
2
|
+
import { Property } from './property';
|
|
3
|
+
/**
|
|
4
|
+
* A class representing the content of a .properties file.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Properties {
|
|
7
|
+
/** Object associating keys with their starting line numbers. */
|
|
8
|
+
keyLineNumbers: KeyLineNumbers;
|
|
9
|
+
/** The collection of property object. */
|
|
10
|
+
collection: Property[];
|
|
11
|
+
/**
|
|
12
|
+
* Add a property object into a properties object collection.
|
|
13
|
+
*
|
|
14
|
+
* @param property - A property object, or undefined.
|
|
15
|
+
*
|
|
16
|
+
* @returns Undefined so that we conveniently overwrite the property object.
|
|
17
|
+
*/
|
|
18
|
+
add(property: Property | undefined): undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Get the JSON (key/value) representation of the properties.
|
|
21
|
+
*
|
|
22
|
+
* @returns A key/value representing the properties of the object.
|
|
23
|
+
*/
|
|
24
|
+
toJson(): KeyValueObject;
|
|
25
|
+
/**
|
|
26
|
+
* Get keys that have collisions (more than one occurrence).
|
|
27
|
+
*/
|
|
28
|
+
getKeyCollisions(): KeyCollisions[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Object associating keys with their line numbers.
|
|
32
|
+
*/
|
|
33
|
+
export declare type KeyLineNumbers = {
|
|
34
|
+
[key: string]: number[];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A class representing key within a .properties file that had collisions (more than one occurrence).
|
|
38
|
+
*/
|
|
39
|
+
export declare class KeyCollisions {
|
|
40
|
+
/** The key with collisions. */
|
|
41
|
+
key: string;
|
|
42
|
+
/** The starting line numbers where collisions are found. */
|
|
43
|
+
startingLineNumbers: number[];
|
|
44
|
+
/**
|
|
45
|
+
* Create a new key collision object.
|
|
46
|
+
*
|
|
47
|
+
* @param key - The key with collisions.
|
|
48
|
+
* @param startingLineNumbers - The starting line numbers where collisions are found.
|
|
49
|
+
*/
|
|
50
|
+
constructor(key: string, startingLineNumbers: number[]);
|
|
51
|
+
/**
|
|
52
|
+
* Get the number of the line from which the value will be used.
|
|
53
|
+
*/
|
|
54
|
+
getApplicableLineNumber(): number;
|
|
55
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KeyCollisions = exports.Properties = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A class representing the content of a .properties file.
|
|
6
|
+
*/
|
|
7
|
+
var Properties = /** @class */ (function () {
|
|
8
|
+
function Properties() {
|
|
9
|
+
/** Object associating keys with their starting line numbers. */
|
|
10
|
+
this.keyLineNumbers = {};
|
|
11
|
+
/** The collection of property object. */
|
|
12
|
+
this.collection = [];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Add a property object into a properties object collection.
|
|
16
|
+
*
|
|
17
|
+
* @param property - A property object, or undefined.
|
|
18
|
+
*
|
|
19
|
+
* @returns Undefined so that we conveniently overwrite the property object.
|
|
20
|
+
*/
|
|
21
|
+
Properties.prototype.add = function (property) {
|
|
22
|
+
var _a;
|
|
23
|
+
if (property === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
property.setKeyAndValue();
|
|
26
|
+
if ((_a = this.keyLineNumbers[property.key]) === null || _a === void 0 ? void 0 : _a.length) {
|
|
27
|
+
this.keyLineNumbers[property.key].push(property.startingLineNumber);
|
|
28
|
+
property.hasKeyCollisions = true;
|
|
29
|
+
property.keyCollisionLines = this.keyLineNumbers[property.key];
|
|
30
|
+
// Remove collision so that we can overwrite it with the latest object.
|
|
31
|
+
this.collection = this.collection.filter(function (existingPropertyObject) { return existingPropertyObject.key !== property.key; });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Initialize the key line numbers.
|
|
35
|
+
this.keyLineNumbers[property.key] = [property.startingLineNumber];
|
|
36
|
+
}
|
|
37
|
+
// Add the property to the collection.
|
|
38
|
+
this.collection.push(property);
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Get the JSON (key/value) representation of the properties.
|
|
43
|
+
*
|
|
44
|
+
* @returns A key/value representing the properties of the object.
|
|
45
|
+
*/
|
|
46
|
+
Properties.prototype.toJson = function () {
|
|
47
|
+
var keyValueObject = {};
|
|
48
|
+
this.collection.forEach(function (property) {
|
|
49
|
+
keyValueObject[property.key] = property.value;
|
|
50
|
+
});
|
|
51
|
+
return keyValueObject;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Get keys that have collisions (more than one occurrence).
|
|
55
|
+
*/
|
|
56
|
+
Properties.prototype.getKeyCollisions = function () {
|
|
57
|
+
var keyCollisions = [];
|
|
58
|
+
for (var _i = 0, _a = Object.entries(this.keyLineNumbers); _i < _a.length; _i++) {
|
|
59
|
+
var _b = _a[_i], key = _b[0], startingLineNumbers = _b[1];
|
|
60
|
+
if (startingLineNumbers.length > 1) {
|
|
61
|
+
keyCollisions.push(new KeyCollisions(key, startingLineNumbers));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return keyCollisions;
|
|
65
|
+
};
|
|
66
|
+
return Properties;
|
|
67
|
+
}());
|
|
68
|
+
exports.Properties = Properties;
|
|
69
|
+
/**
|
|
70
|
+
* A class representing key within a .properties file that had collisions (more than one occurrence).
|
|
71
|
+
*/
|
|
72
|
+
var KeyCollisions = /** @class */ (function () {
|
|
73
|
+
/**
|
|
74
|
+
* Create a new key collision object.
|
|
75
|
+
*
|
|
76
|
+
* @param key - The key with collisions.
|
|
77
|
+
* @param startingLineNumbers - The starting line numbers where collisions are found.
|
|
78
|
+
*/
|
|
79
|
+
function KeyCollisions(key, startingLineNumbers) {
|
|
80
|
+
this.key = key;
|
|
81
|
+
this.startingLineNumbers = startingLineNumbers;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the number of the line from which the value will be used.
|
|
85
|
+
*/
|
|
86
|
+
KeyCollisions.prototype.getApplicableLineNumber = function () {
|
|
87
|
+
return this.startingLineNumbers.slice(-1)[0];
|
|
88
|
+
};
|
|
89
|
+
return KeyCollisions;
|
|
90
|
+
}());
|
|
91
|
+
exports.KeyCollisions = KeyCollisions;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object representing a line from the content of .properties file.
|
|
3
|
+
*/
|
|
4
|
+
export declare class PropertyLine {
|
|
5
|
+
/** The line content, minus the trailing \ that identifies that the line continues. */
|
|
6
|
+
content: string;
|
|
7
|
+
/** Is the line object a continuation from a previous line? */
|
|
8
|
+
isMultiline: boolean;
|
|
9
|
+
/** True if the line continues, otherwise false. */
|
|
10
|
+
continues: boolean;
|
|
11
|
+
/** True if the line is a comment, otherwise false. */
|
|
12
|
+
isComment: boolean;
|
|
13
|
+
/** True if the line is blank, otherwise false. */
|
|
14
|
+
isBlank: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Create a new line object.
|
|
17
|
+
*
|
|
18
|
+
* @param line - The raw content of a line.
|
|
19
|
+
* @param isMultiline - Is the line spreading on multiple lines?
|
|
20
|
+
*/
|
|
21
|
+
constructor(line: string, isMultiline: boolean);
|
|
22
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PropertyLine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Object representing a line from the content of .properties file.
|
|
6
|
+
*/
|
|
7
|
+
var PropertyLine = /** @class */ (function () {
|
|
8
|
+
/**
|
|
9
|
+
* Create a new line object.
|
|
10
|
+
*
|
|
11
|
+
* @param line - The raw content of a line.
|
|
12
|
+
* @param isMultiline - Is the line spreading on multiple lines?
|
|
13
|
+
*/
|
|
14
|
+
function PropertyLine(line, isMultiline) {
|
|
15
|
+
/** True if the line continues, otherwise false. */
|
|
16
|
+
this.continues = false;
|
|
17
|
+
/** True if the line is a comment, otherwise false. */
|
|
18
|
+
this.isComment = false;
|
|
19
|
+
/** True if the line is blank, otherwise false. */
|
|
20
|
+
this.isBlank = false;
|
|
21
|
+
this.content = line.trimStart();
|
|
22
|
+
this.isMultiline = isMultiline;
|
|
23
|
+
if (!this.content.length) {
|
|
24
|
+
// Line is blank.
|
|
25
|
+
this.isBlank = true;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (!this.isMultiline) {
|
|
29
|
+
// Line is a comment.
|
|
30
|
+
this.isComment = !!this.content.match(/^[!#]/);
|
|
31
|
+
}
|
|
32
|
+
if (!this.isComment) {
|
|
33
|
+
// Otherwise, check if the line continues on the next line.
|
|
34
|
+
var backslashMatch = this.content.match(/(?<backslashes>\\+)$/);
|
|
35
|
+
if (backslashMatch === null || backslashMatch === void 0 ? void 0 : backslashMatch.groups) {
|
|
36
|
+
// If the number of backslashes is odd, the line continues, otherwise it doesn't.
|
|
37
|
+
this.continues = !!(backslashMatch.groups.backslashes.length % 2);
|
|
38
|
+
if (this.continues) {
|
|
39
|
+
// Remove the trailing slash so that we can concatenate the line with the next one.
|
|
40
|
+
this.content = this.content.slice(0, -1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return PropertyLine;
|
|
47
|
+
}());
|
|
48
|
+
exports.PropertyLine = PropertyLine;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PropertyLine } from './property-line';
|
|
2
|
+
/**
|
|
3
|
+
* Object representing a property (key/value).
|
|
4
|
+
*/
|
|
5
|
+
export declare class Property {
|
|
6
|
+
/** The line number at which the property starts. */
|
|
7
|
+
startingLineNumber: number;
|
|
8
|
+
/** The content of one or multiple lines when applicable. */
|
|
9
|
+
linesContent: string;
|
|
10
|
+
/** Positions of the newline characters if any. */
|
|
11
|
+
newlinePositions: number[];
|
|
12
|
+
/** Starting line numbers of property objects with the same key. */
|
|
13
|
+
keyCollisionLines: number[];
|
|
14
|
+
/** The starting position of the delimiter separating the key from the value. */
|
|
15
|
+
delimiterPosition: number | undefined;
|
|
16
|
+
/** The length of the delimiter, including its whitespace characters. */
|
|
17
|
+
delimiterLength: number | undefined;
|
|
18
|
+
/** The property key, including its escaped characters. */
|
|
19
|
+
escapedKey: string;
|
|
20
|
+
/** The property value, including its escaped characters. */
|
|
21
|
+
escapedValue: string;
|
|
22
|
+
/** The property key (unescaped). */
|
|
23
|
+
key: string;
|
|
24
|
+
/** The property value (unescaped). */
|
|
25
|
+
value: string;
|
|
26
|
+
/** Was the property's key used more than once? */
|
|
27
|
+
hasKeyCollisions: boolean;
|
|
28
|
+
/** Does the key definition spread across multiple lines? */
|
|
29
|
+
private hasMultilineKey;
|
|
30
|
+
/** Is the key empty? */
|
|
31
|
+
private hasNoKey;
|
|
32
|
+
/** Is the value empty? */
|
|
33
|
+
private hasNoValue;
|
|
34
|
+
/**
|
|
35
|
+
* Create a new property object.
|
|
36
|
+
*
|
|
37
|
+
* @param propertyLine - A property line object.
|
|
38
|
+
* @param startingLineNumber - The line number at which the property starts.
|
|
39
|
+
*/
|
|
40
|
+
constructor(propertyLine: PropertyLine, startingLineNumber: number);
|
|
41
|
+
/**
|
|
42
|
+
* Add the a line to a multiline property object.
|
|
43
|
+
*
|
|
44
|
+
* @param propertyLine - A property line object.
|
|
45
|
+
*/
|
|
46
|
+
addLine(propertyLine: PropertyLine): void;
|
|
47
|
+
/**
|
|
48
|
+
* Set the property's key and value.
|
|
49
|
+
*/
|
|
50
|
+
setKeyAndValue(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Unescape the content from either key or value of a property.
|
|
53
|
+
*
|
|
54
|
+
* @param escapedContent - The content to unescape.
|
|
55
|
+
* @param startingLineNumber - The starting line number of the content being unescaped.
|
|
56
|
+
*
|
|
57
|
+
* @returns The unescaped content.
|
|
58
|
+
*/
|
|
59
|
+
unescape(escapedContent: string, startingLineNumber: number): string;
|
|
60
|
+
/**
|
|
61
|
+
* Find the delimiting characters separating the key from the value.
|
|
62
|
+
*/
|
|
63
|
+
private findDelimiter;
|
|
64
|
+
}
|
package/lib/property.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Property = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Object representing a property (key/value).
|
|
6
|
+
*/
|
|
7
|
+
var Property = /** @class */ (function () {
|
|
8
|
+
/**
|
|
9
|
+
* Create a new property object.
|
|
10
|
+
*
|
|
11
|
+
* @param propertyLine - A property line object.
|
|
12
|
+
* @param startingLineNumber - The line number at which the property starts.
|
|
13
|
+
*/
|
|
14
|
+
function Property(propertyLine, startingLineNumber) {
|
|
15
|
+
/** Positions of the newline characters if any. */
|
|
16
|
+
this.newlinePositions = [];
|
|
17
|
+
/** Starting line numbers of property objects with the same key. */
|
|
18
|
+
this.keyCollisionLines = [];
|
|
19
|
+
/** The property key, including its escaped characters. */
|
|
20
|
+
this.escapedKey = '';
|
|
21
|
+
/** The property value, including its escaped characters. */
|
|
22
|
+
this.escapedValue = '';
|
|
23
|
+
/** The property key (unescaped). */
|
|
24
|
+
this.key = '';
|
|
25
|
+
/** The property value (unescaped). */
|
|
26
|
+
this.value = '';
|
|
27
|
+
/** Was the property's key used more than once? */
|
|
28
|
+
this.hasKeyCollisions = false;
|
|
29
|
+
/** Does the key definition spread across multiple lines? */
|
|
30
|
+
this.hasMultilineKey = false;
|
|
31
|
+
/** Is the key empty? */
|
|
32
|
+
this.hasNoKey = false;
|
|
33
|
+
/** Is the value empty? */
|
|
34
|
+
this.hasNoValue = false;
|
|
35
|
+
this.linesContent = propertyLine.content;
|
|
36
|
+
this.startingLineNumber = startingLineNumber;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Add the a line to a multiline property object.
|
|
40
|
+
*
|
|
41
|
+
* @param propertyLine - A property line object.
|
|
42
|
+
*/
|
|
43
|
+
Property.prototype.addLine = function (propertyLine) {
|
|
44
|
+
if (this.linesContent.length) {
|
|
45
|
+
this.newlinePositions.push(this.linesContent.length);
|
|
46
|
+
}
|
|
47
|
+
this.linesContent += propertyLine.content;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Set the property's key and value.
|
|
51
|
+
*/
|
|
52
|
+
Property.prototype.setKeyAndValue = function () {
|
|
53
|
+
this.findDelimiter();
|
|
54
|
+
if (this.delimiterPosition !== undefined && this.delimiterLength !== undefined) {
|
|
55
|
+
// Set key if present.
|
|
56
|
+
if (!this.hasNoKey) {
|
|
57
|
+
this.escapedKey = this.linesContent.substring(0, this.delimiterPosition);
|
|
58
|
+
this.key = this.unescape(this.escapedKey, this.startingLineNumber);
|
|
59
|
+
}
|
|
60
|
+
// Set value if present.
|
|
61
|
+
if (!this.hasNoValue) {
|
|
62
|
+
this.escapedValue = this.linesContent.substring(this.delimiterPosition + this.delimiterLength);
|
|
63
|
+
this.value = this.unescape(this.escapedValue, this.startingLineNumber);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (this.hasNoValue) {
|
|
67
|
+
// Set key if present (no delimiter).
|
|
68
|
+
this.escapedKey = this.linesContent;
|
|
69
|
+
this.key = this.unescape(this.escapedKey, this.startingLineNumber);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Unescape the content from either key or value of a property.
|
|
74
|
+
*
|
|
75
|
+
* @param escapedContent - The content to unescape.
|
|
76
|
+
* @param startingLineNumber - The starting line number of the content being unescaped.
|
|
77
|
+
*
|
|
78
|
+
* @returns The unescaped content.
|
|
79
|
+
*/
|
|
80
|
+
Property.prototype.unescape = function (escapedContent, startingLineNumber) {
|
|
81
|
+
var unescapedContent = '';
|
|
82
|
+
for (var position = 0, character = escapedContent[0]; position < escapedContent.length; position++, character = escapedContent[position]) {
|
|
83
|
+
if (character === '\\') {
|
|
84
|
+
var nextCharacter = escapedContent[position + 1];
|
|
85
|
+
if (nextCharacter === 'f') {
|
|
86
|
+
// Formfeed/
|
|
87
|
+
unescapedContent += '\f';
|
|
88
|
+
position++;
|
|
89
|
+
}
|
|
90
|
+
else if (nextCharacter === 'n') {
|
|
91
|
+
// Newline.
|
|
92
|
+
unescapedContent += '\n';
|
|
93
|
+
position++;
|
|
94
|
+
}
|
|
95
|
+
else if (nextCharacter === 'r') {
|
|
96
|
+
// Carriage return.
|
|
97
|
+
unescapedContent += '\r';
|
|
98
|
+
position++;
|
|
99
|
+
}
|
|
100
|
+
else if (nextCharacter === 't') {
|
|
101
|
+
// Tab.
|
|
102
|
+
unescapedContent += '\t';
|
|
103
|
+
position++;
|
|
104
|
+
}
|
|
105
|
+
else if (nextCharacter === 'u') {
|
|
106
|
+
// Unicode character.
|
|
107
|
+
var codePoint = escapedContent.substring(position + 2, position + 6);
|
|
108
|
+
if (!/[0-9a-f]{4}/i.test(codePoint)) {
|
|
109
|
+
// Code point can only be within Unicode's Multilingual Plane (BMP).
|
|
110
|
+
throw new Error("malformed escaped unicode characters '\\u".concat(codePoint, "' in property starting at line ").concat(startingLineNumber));
|
|
111
|
+
}
|
|
112
|
+
unescapedContent += String.fromCharCode(parseInt(codePoint, 16));
|
|
113
|
+
position += 5;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Otherwise the escape character is not required.
|
|
117
|
+
unescapedContent += nextCharacter;
|
|
118
|
+
position++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// When there is \, simply add the character.
|
|
123
|
+
unescapedContent += character;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return unescapedContent;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Find the delimiting characters separating the key from the value.
|
|
130
|
+
*/
|
|
131
|
+
Property.prototype.findDelimiter = function () {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
// If the delimiter was already found, skip.
|
|
134
|
+
if (this.hasNoKey || this.hasNoValue || this.delimiterPosition) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
for (var position = 0, character = this.linesContent[0]; position < this.linesContent.length; position++, character = this.linesContent[position]) {
|
|
138
|
+
// If the character is not a delimiter, check the next one.
|
|
139
|
+
if (!/[ \t\f=:]/.test(character)) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// Check if the delimiter might be escaped.
|
|
143
|
+
var prefix = !position ? '' : this.linesContent.substring(0, position);
|
|
144
|
+
if (prefix.length) {
|
|
145
|
+
var backslashMatch = prefix.match(/(?<backslashes>\\+)$/);
|
|
146
|
+
if (backslashMatch === null || backslashMatch === void 0 ? void 0 : backslashMatch.groups) {
|
|
147
|
+
var delimiterIsEscaped = !!(backslashMatch.groups.backslashes.length % 2);
|
|
148
|
+
if (delimiterIsEscaped) {
|
|
149
|
+
// If the delimiter is escaped, check the next character.
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
var delimiter = '';
|
|
155
|
+
this.delimiterPosition = position;
|
|
156
|
+
this.hasMultilineKey = !!(this.newlinePositions.length && this.newlinePositions[0] > position);
|
|
157
|
+
// Check if the delimiter starts with a whitespace.
|
|
158
|
+
var nextContent = this.linesContent.substring(position);
|
|
159
|
+
var leadingWhitespaceMatch = nextContent.match(/^(?<whitespace>\s+)/);
|
|
160
|
+
var leadingWhitespace = ((_a = leadingWhitespaceMatch === null || leadingWhitespaceMatch === void 0 ? void 0 : leadingWhitespaceMatch.groups) === null || _a === void 0 ? void 0 : _a.whitespace) || '';
|
|
161
|
+
// If there is a whitespace, move to the next character.
|
|
162
|
+
if (leadingWhitespace.length) {
|
|
163
|
+
delimiter += leadingWhitespace;
|
|
164
|
+
nextContent = nextContent.substring(leadingWhitespace.length);
|
|
165
|
+
}
|
|
166
|
+
// Check if there is an equal or colon character.
|
|
167
|
+
if (/[=:]/.test(nextContent[0])) {
|
|
168
|
+
delimiter += nextContent[0];
|
|
169
|
+
nextContent = nextContent.substring(1);
|
|
170
|
+
// If an equal or colon character was found, try to get trailing whitespace.
|
|
171
|
+
var trailingWhitespaceMatch = nextContent.match(/^(?<whitespace>\s+)/);
|
|
172
|
+
var trailingWhitespace = ((_b = trailingWhitespaceMatch === null || trailingWhitespaceMatch === void 0 ? void 0 : trailingWhitespaceMatch.groups) === null || _b === void 0 ? void 0 : _b.whitespace) || '';
|
|
173
|
+
delimiter += trailingWhitespace;
|
|
174
|
+
}
|
|
175
|
+
this.delimiterLength = delimiter.length;
|
|
176
|
+
// If the line starts with a delimiter, the property has no key.
|
|
177
|
+
if (!position) {
|
|
178
|
+
this.hasNoKey = true;
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
// If there was no delimiter found, the property has no value.
|
|
183
|
+
if (this.delimiterPosition === undefined) {
|
|
184
|
+
this.hasNoValue = true;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// If the delimiter is after the first newline, mark the key as multiline.
|
|
188
|
+
if (this.newlinePositions.length) {
|
|
189
|
+
var firstLinePosition = this.newlinePositions[0];
|
|
190
|
+
if (firstLinePosition > this.delimiterPosition) {
|
|
191
|
+
this.hasMultilineKey = true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
return Property;
|
|
197
|
+
}());
|
|
198
|
+
exports.Property = Property;
|
package/package.json
CHANGED
|
@@ -1,29 +1,70 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "properties-file",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "properties-file",
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"description": ".properties file parser, JSON converter and Webpack loader.",
|
|
5
|
+
"author": "Avansai (https://avansai.com)",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Avansai/properties-file.git"
|
|
9
|
+
},
|
|
10
|
+
"main": "lib/index.js",
|
|
11
|
+
"types": "lib/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./lib/index.js",
|
|
14
|
+
"./content": "./lib/content/index.js",
|
|
15
|
+
"./webpack-loader": "./lib/loader/webpack.js"
|
|
16
|
+
},
|
|
17
|
+
"typesVersions": {
|
|
18
|
+
"*": {
|
|
19
|
+
"content": [
|
|
20
|
+
"lib/content/index.d.ts"
|
|
21
|
+
],
|
|
22
|
+
"webpack-loader": [
|
|
23
|
+
"lib/loader/webpack.d.ts"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"files": [
|
|
29
|
+
"lib"
|
|
30
|
+
],
|
|
31
|
+
"keywords": [
|
|
32
|
+
".properties",
|
|
33
|
+
"properties",
|
|
34
|
+
".properties file",
|
|
35
|
+
"properties file",
|
|
36
|
+
"parser",
|
|
37
|
+
"Java",
|
|
38
|
+
"intl",
|
|
39
|
+
"i18n",
|
|
40
|
+
"properties Webpack loader",
|
|
41
|
+
"Webpack loader",
|
|
42
|
+
"internationalization"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "rm -Rf ./lib && tsc && npm run add-import-type && npm run lint && npm test",
|
|
46
|
+
"add-import-type": "node ./src/add-import-type",
|
|
47
|
+
"test": "jest --coverage",
|
|
48
|
+
"tmp-test": "tsc && node ./lib/tmp.js",
|
|
49
|
+
"lint": "eslint . --fix",
|
|
50
|
+
"prettier-check": "prettier --check .",
|
|
51
|
+
"prettier-fix": "prettier --write .",
|
|
52
|
+
"release": "dotenv -- release-it --only-version"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@release-it/conventional-changelog": "^5.0.0",
|
|
56
|
+
"@types/jest": "^28.1.3",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
|
58
|
+
"@typescript-eslint/parser": "^5.30.0",
|
|
59
|
+
"dotenv-cli": "^5.1.0",
|
|
60
|
+
"eslint": "^8.18.0",
|
|
61
|
+
"eslint-config-prettier": "^8.5.0",
|
|
62
|
+
"eslint-plugin-jest": "^26.5.3",
|
|
63
|
+
"jest": "^28.1.2",
|
|
64
|
+
"prettier": "2.7.1",
|
|
65
|
+
"release-it": "^15.1.1",
|
|
66
|
+
"ts-jest": "^28.0.5",
|
|
67
|
+
"ts-node": "^10.8.1",
|
|
68
|
+
"typescript": "^4.7.4"
|
|
69
|
+
}
|
|
70
|
+
}
|
package/.npmignore
DELETED
package/index.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
exports.parse = function(string) {
|
|
2
|
-
var object = {};
|
|
3
|
-
string.split(/(\n|\r\n)/).forEach(function(s) {
|
|
4
|
-
s = s.trim();
|
|
5
|
-
if(!s) {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
var eqIndex = s.indexOf("=");
|
|
9
|
-
if(eqIndex < 0) {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
var key = s.substr(0, eqIndex).trim();
|
|
13
|
-
var value = s.substr(eqIndex + 1).trim();
|
|
14
|
-
object[key] = value;
|
|
15
|
-
});
|
|
16
|
-
return object;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
exports.stringify = function(object) {
|
|
20
|
-
var strings = [];
|
|
21
|
-
for(var k in object) {
|
|
22
|
-
strings.push(k + "=" + object[k]);
|
|
23
|
-
}
|
|
24
|
-
return strings.join("\n");
|
|
25
|
-
};
|
package/readme.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Just parse .properties files which used in firefox localization
|
|
2
|
-
|
|
3
|
-
## Install
|
|
4
|
-
|
|
5
|
-
`npm i properties-file`
|
|
6
|
-
|
|
7
|
-
## Usage
|
|
8
|
-
|
|
9
|
-
### Parse .properties file
|
|
10
|
-
|
|
11
|
-
```js
|
|
12
|
-
var parser = require("properties-file");
|
|
13
|
-
|
|
14
|
-
var object = parser.parse(propertiesFileContents);
|
|
15
|
-
// object with key value strings
|
|
16
|
-
// for example if you have .properties file:
|
|
17
|
-
// str1=text1
|
|
18
|
-
// str2=text2
|
|
19
|
-
// you will get object like this:
|
|
20
|
-
// {
|
|
21
|
-
// "str1": "text1",
|
|
22
|
-
// "str2": "text2"
|
|
23
|
-
// }
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Generate properties file from object
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
var parser = require("properties-file");
|
|
30
|
-
|
|
31
|
-
var string = parser.stringify({
|
|
32
|
-
str1: "text1",
|
|
33
|
-
str2: "text2"
|
|
34
|
-
});
|
|
35
|
-
// string will be
|
|
36
|
-
// str1=text1
|
|
37
|
-
// str2=text2
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
enable_speed_dial=Speed Dial aktivieren
|
|
2
|
-
max_groups=Maximale Dials in Beliebt Gruppe:
|
|
3
|
-
thumbnails=Vorschaubilder
|
|
4
|
-
groups=Gruppen
|
|
5
|
-
default=Standard
|
|
6
|
-
manage_groups=Gruppen verwalten
|
|
7
|
-
enable_most_visited=Am Häufigsten Besucht aktivieren
|
|
8
|
-
default_group=Standard:
|
|
9
|
-
big=Groß
|
|
10
|
-
medium=Mittel
|
|
11
|
-
small=Klein
|
|
12
|
-
custom=Benutzerdefiniert
|
|
13
|
-
list=Liste
|
|
14
|
-
enable_recently_closed=Kürzlich Geschlossen aktivieren
|
|
15
|
-
show_last=Zeige letzte:
|
|
16
|
-
restore_removed=Stelle gelöschte wieder her
|
|
17
|
-
all_group_title=Beliebt
|
|
18
|
-
last_selected_group_title=Zuletzt Ausgewählt
|
|
19
|
-
yes=Ja
|
|
20
|
-
no=Nein
|
|
21
|
-
sure=Sicher?
|
|
22
|
-
options=Optionen
|
|
23
|
-
expand_all=Alle erweitern
|
|
24
|
-
hide_all=Alle ausblenden
|
|
25
|
-
cache_life_time=Synchronisieren alle:
|
|
26
|
-
hours=Stunde(n)
|
|
27
|
-
synchronization=Synchronisierung
|
|
28
|
-
sync_now=Statistiken aktualisieren
|
|
29
|
-
done=Fertig!
|
|
30
|
-
global_options=Globale Optionen
|
|
31
|
-
make_as_homepage=Als Startseite festlegen
|
|
32
|
-
number_of_columns=Spaltenanzahl:
|
|
33
|
-
columns_auto=Automatisch
|
|
34
|
-
change_background=Hintergrund ändern
|
|
35
|
-
restore_previous_session=Letzte Sitzung wiederherstellen
|
|
36
|
-
do_not_display_migrate=Diese Meldung nicht noch einmal zeigen
|
|
37
|
-
start_migrate=Importieren starten
|
|
38
|
-
cancel_migrate=Importieren abbrechen
|
|
39
|
-
migrate_message=Du kannst Daten von Dials/Gruppen aus Erweiterungen importieren:
|
|
40
|
-
import=Importieren
|
|
41
|
-
import_available=Verfügbare importieren
|
|
42
|
-
most_visited_display_order=Sortieren nach
|
|
43
|
-
most_visited_visits=Besuche
|
|
44
|
-
most_visited_date=Datum
|
|
45
|
-
rate_message=Bitte schreibe deine Beurteilung und gib uns eine Bewertung.
|
|
46
|
-
menu_hot_key=HotKey: Drücke Leertaste
|
|
47
|
-
hot_key_number_tpl=HotKey: Drücke %number%
|
|
48
|
-
found_data_for_sync=Daten für Synchronisierung gefunden
|
|
49
|
-
sync_updates_now=Jetzt synchronisieren
|
|
50
|
-
skip_sync_updates=Synchronisieren überspringen
|
|
51
|
-
rate_message_fvd_speed_dial=FVD Speed Dial
|
|
52
|
-
rate_message_appreciate=Wir wissen deine Hilfe zu schätzen
|
|
53
|
-
can_turn_off_newtab_msg=Hinweis: Du kannst FVD Speed Dial in einer neuen Tab-Seite deaktivieren und nur auf Knopfdruck starten.
|
|
54
|
-
can_turn_off_newtab_opt_app=Optionen > Erscheinungsbild
|
|
55
|
-
can_turn_off_newtab_got_it=Ich habe es verstanden. Danke.
|
|
56
|
-
poweroff_button_title=Power Off
|
|
57
|
-
showhide_button_title=Show/Hide
|
|
58
|
-
number_of_rows=Number of Rows:
|
|
59
|
-
cant_arrange_in_popular=You can rearrange dials in ANY group except in "Popular Group". This group arranges dials by clicks popularity.
|
|
60
|
-
install_eversync_title_head=In order to use Sync you need to install EverSync extension
|
|
61
|
-
install_eversync_title_head_sub=To use Fvd Sync you need to install additional addon - EverSync extension:
|
|
62
|
-
install_eversync_f1=Sync your Dials
|
|
63
|
-
install_eversync_f2=Sync your Groups
|
|
64
|
-
install_eversync_f3=Use it between Firefox, Chrome, Opera and Mobile Phones, Different PCs.
|
|
65
|
-
install_eversync_f4=Sync your Firefox, Chrome, Opera, Mobile Phones Bookmarks
|
|
66
|
-
install_eversync_f5=Sync your Folders.
|
|
67
|
-
install_eversync_f6=Manage Bookmarks and Dials
|
|
68
|
-
install_eversync_f7=Copy Bookmarks to FVD Speed Dial and From Speed Dial to Bookmarks
|
|
69
|
-
install_eversync_f8=Trash / Favorites / Private / Archive / Advanced Search and more additional features.
|
|
70
|
-
install_eversync_f9=Server auto backups and restore.
|
|
71
|
-
install_eversync_install=Install from Mozilla.org
|
|
72
|
-
install_eversync_installing=Installation...
|
package/test/test.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
var
|
|
2
|
-
parser = require("../index"),
|
|
3
|
-
should = require("should"),
|
|
4
|
-
fs = require("fs");
|
|
5
|
-
|
|
6
|
-
describe("Common", function() {
|
|
7
|
-
it("should parse test file", function() {
|
|
8
|
-
var raw = fs.readFileSync(__dirname + "/test.properties", "utf8");
|
|
9
|
-
var res = parser.parse(raw);
|
|
10
|
-
var strRes = parser.stringify(res);
|
|
11
|
-
strRes.should.equal(fs.readFileSync(__dirname + "/stringify_result.properties", "utf8"));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("should stringify object", function() {
|
|
15
|
-
var obj = {
|
|
16
|
-
str1: "text1",
|
|
17
|
-
str2: "text2"
|
|
18
|
-
};
|
|
19
|
-
var res = parser.stringify(obj);
|
|
20
|
-
res.should.equal("str1=text1\nstr2=text2");
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
package/test/test.properties
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
enable_speed_dial=Speed Dial aktivieren
|
|
2
|
-
max_groups=Maximale Dials in Beliebt Gruppe:
|
|
3
|
-
thumbnails=Vorschaubilder
|
|
4
|
-
groups=Gruppen
|
|
5
|
-
default=Standard
|
|
6
|
-
manage_groups=Gruppen verwalten
|
|
7
|
-
enable_most_visited=Am Häufigsten Besucht aktivieren
|
|
8
|
-
default_group=Standard:
|
|
9
|
-
big=Groß
|
|
10
|
-
medium=Mittel
|
|
11
|
-
small=Klein
|
|
12
|
-
custom=Benutzerdefiniert
|
|
13
|
-
list=Liste
|
|
14
|
-
enable_recently_closed=Kürzlich Geschlossen aktivieren
|
|
15
|
-
show_last=Zeige letzte:
|
|
16
|
-
restore_removed=Stelle gelöschte wieder her
|
|
17
|
-
all_group_title=Beliebt
|
|
18
|
-
last_selected_group_title=Zuletzt Ausgewählt
|
|
19
|
-
yes=Ja
|
|
20
|
-
no=Nein
|
|
21
|
-
sure=Sicher?
|
|
22
|
-
options=Optionen
|
|
23
|
-
expand_all=Alle erweitern
|
|
24
|
-
hide_all=Alle ausblenden
|
|
25
|
-
cache_life_time=Synchronisieren alle:
|
|
26
|
-
hours=Stunde(n)
|
|
27
|
-
synchronization=Synchronisierung
|
|
28
|
-
sync_now=Statistiken aktualisieren
|
|
29
|
-
done=Fertig!
|
|
30
|
-
global_options=Globale Optionen
|
|
31
|
-
make_as_homepage=Als Startseite festlegen
|
|
32
|
-
number_of_columns=Spaltenanzahl:
|
|
33
|
-
columns_auto=Automatisch
|
|
34
|
-
change_background=Hintergrund ändern
|
|
35
|
-
restore_previous_session=Letzte Sitzung wiederherstellen
|
|
36
|
-
do_not_display_migrate=Diese Meldung nicht noch einmal zeigen
|
|
37
|
-
start_migrate=Importieren starten
|
|
38
|
-
cancel_migrate=Importieren abbrechen
|
|
39
|
-
migrate_message=Du kannst Daten von Dials/Gruppen aus Erweiterungen importieren:
|
|
40
|
-
import=Importieren
|
|
41
|
-
import_available=Verfügbare importieren
|
|
42
|
-
most_visited_display_order=Sortieren nach
|
|
43
|
-
most_visited_visits=Besuche
|
|
44
|
-
most_visited_date=Datum
|
|
45
|
-
rate_message=Bitte schreibe deine Beurteilung und gib uns eine Bewertung.
|
|
46
|
-
menu_hot_key=HotKey: Drücke Leertaste
|
|
47
|
-
hot_key_number_tpl=HotKey: Drücke %number%
|
|
48
|
-
found_data_for_sync=Daten für Synchronisierung gefunden
|
|
49
|
-
sync_updates_now=Jetzt synchronisieren
|
|
50
|
-
skip_sync_updates=Synchronisieren überspringen
|
|
51
|
-
rate_message_fvd_speed_dial=FVD Speed Dial
|
|
52
|
-
rate_message_appreciate=Wir wissen deine Hilfe zu schätzen
|
|
53
|
-
can_turn_off_newtab_msg=Hinweis: Du kannst FVD Speed Dial in einer neuen Tab-Seite deaktivieren und nur auf Knopfdruck starten.
|
|
54
|
-
can_turn_off_newtab_opt_app=Optionen > Erscheinungsbild
|
|
55
|
-
can_turn_off_newtab_got_it=Ich habe es verstanden. Danke.
|
|
56
|
-
poweroff_button_title=Power Off
|
|
57
|
-
showhide_button_title=Show/Hide
|
|
58
|
-
number_of_rows=Number of Rows:
|
|
59
|
-
cant_arrange_in_popular=You can rearrange dials in ANY group except in "Popular Group". This group arranges dials by clicks popularity.
|
|
60
|
-
install_eversync_title_head = In order to use Sync you need to install EverSync extension
|
|
61
|
-
install_eversync_title_head_sub = To use Fvd Sync you need to install additional addon - EverSync extension:
|
|
62
|
-
install_eversync_f1 = Sync your Dials
|
|
63
|
-
install_eversync_f2 = Sync your Groups
|
|
64
|
-
install_eversync_f3 = Use it between Firefox, Chrome, Opera and Mobile Phones, Different PCs.
|
|
65
|
-
install_eversync_f4 = Sync your Firefox, Chrome, Opera, Mobile Phones Bookmarks
|
|
66
|
-
install_eversync_f5 = Sync your Folders.
|
|
67
|
-
install_eversync_f6 = Manage Bookmarks and Dials
|
|
68
|
-
install_eversync_f7 = Copy Bookmarks to FVD Speed Dial and From Speed Dial to Bookmarks
|
|
69
|
-
install_eversync_f8 = Trash / Favorites / Private / Archive / Advanced Search and more additional features.
|
|
70
|
-
install_eversync_f9 = Server auto backups and restore.
|
|
71
|
-
install_eversync_install = Install from Mozilla.org
|
|
72
|
-
install_eversync_installing = Installation...
|