properties-file 3.7.0 → 5.0.0

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.
Files changed (72) hide show
  1. package/README.md +253 -285
  2. package/dist/cjs/bundler/bun.d.ts +10 -0
  3. package/dist/cjs/bundler/bun.js +1 -0
  4. package/dist/cjs/bundler/esbuild.d.ts +11 -0
  5. package/dist/cjs/bundler/esbuild.js +1 -0
  6. package/dist/cjs/bundler/rollup.d.ts +11 -0
  7. package/dist/cjs/bundler/rollup.js +1 -0
  8. package/dist/cjs/bundler/webpack.d.ts +12 -0
  9. package/dist/cjs/bundler/webpack.js +1 -0
  10. package/dist/cjs/editor/index.d.ts +76 -93
  11. package/dist/cjs/editor/index.js +1 -1
  12. package/dist/cjs/escape/index.js +1 -1
  13. package/dist/cjs/index.d.ts +0 -1
  14. package/dist/cjs/index.js +1 -1
  15. package/dist/cjs/package.json +1 -1
  16. package/dist/cjs/parse-properties.d.ts +22 -0
  17. package/dist/cjs/parse-properties.js +1 -0
  18. package/dist/cjs/parser/index.d.ts +3 -0
  19. package/dist/cjs/parser/index.js +1 -0
  20. package/dist/cjs/parser/nodes.d.ts +113 -0
  21. package/dist/cjs/parser/nodes.js +1 -0
  22. package/dist/cjs/parser/normalize.d.ts +12 -0
  23. package/dist/cjs/parser/normalize.js +1 -0
  24. package/dist/cjs/parser/parse.d.ts +23 -0
  25. package/dist/cjs/parser/parse.js +1 -0
  26. package/dist/cjs/parser/properties.d.ts +93 -0
  27. package/dist/cjs/parser/properties.js +1 -0
  28. package/dist/cjs/unescape/index.d.ts +4 -0
  29. package/dist/cjs/unescape/index.js +1 -1
  30. package/dist/esm/bundler/bun.d.ts +10 -0
  31. package/dist/esm/bundler/bun.js +1 -0
  32. package/dist/esm/bundler/esbuild.d.ts +11 -0
  33. package/dist/esm/bundler/esbuild.js +1 -0
  34. package/dist/esm/bundler/rollup.d.ts +11 -0
  35. package/dist/esm/bundler/rollup.js +1 -0
  36. package/dist/esm/bundler/webpack.d.ts +12 -0
  37. package/dist/esm/editor/index.d.ts +76 -93
  38. package/dist/esm/editor/index.js +1 -1
  39. package/dist/esm/escape/index.js +1 -1
  40. package/dist/esm/index.d.ts +0 -1
  41. package/dist/esm/index.js +1 -1
  42. package/dist/esm/parse-properties.d.ts +22 -0
  43. package/dist/esm/parse-properties.js +1 -0
  44. package/dist/esm/parser/index.d.ts +3 -0
  45. package/dist/esm/parser/index.js +1 -0
  46. package/dist/esm/parser/nodes.d.ts +113 -0
  47. package/dist/esm/parser/nodes.js +1 -0
  48. package/dist/esm/parser/normalize.d.ts +12 -0
  49. package/dist/esm/parser/normalize.js +1 -0
  50. package/dist/esm/parser/parse.d.ts +23 -0
  51. package/dist/esm/parser/parse.js +1 -0
  52. package/dist/esm/parser/properties.d.ts +93 -0
  53. package/dist/esm/parser/properties.js +1 -0
  54. package/dist/esm/unescape/index.d.ts +4 -0
  55. package/dist/esm/unescape/index.js +1 -1
  56. package/package.json +81 -30
  57. package/dist/cjs/loader/webpack.d.ts +0 -12
  58. package/dist/cjs/loader/webpack.js +0 -1
  59. package/dist/cjs/properties.d.ts +0 -96
  60. package/dist/cjs/properties.js +0 -1
  61. package/dist/cjs/property-line.d.ts +0 -22
  62. package/dist/cjs/property-line.js +0 -1
  63. package/dist/cjs/property.d.ts +0 -81
  64. package/dist/cjs/property.js +0 -1
  65. package/dist/esm/loader/webpack.d.ts +0 -12
  66. package/dist/esm/properties.d.ts +0 -96
  67. package/dist/esm/properties.js +0 -1
  68. package/dist/esm/property-line.d.ts +0 -22
  69. package/dist/esm/property-line.js +0 -1
  70. package/dist/esm/property.d.ts +0 -81
  71. package/dist/esm/property.js +0 -1
  72. /package/dist/esm/{loader → bundler}/webpack.js +0 -0
package/README.md CHANGED
@@ -1,285 +1,253 @@
1
- # properties-file
2
-
3
- [![License](https://img.shields.io/npm/l/make-coverage-badge.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
4
- [![Download Stats](https://img.shields.io/npm/dw/properties-file.svg?color=brightgreen)](https://www.npmjs.com/package/properties-file)
5
- ![Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen.svg)
6
- [![Package Size](https://deno.bundlejs.com/badge?q=properties-file@latest&treeshake=[*])](https://bundlejs.com/?q=properties-file@latest&treeshake=[*])
7
- ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)
8
-
9
- `.properties` file parser, editor, formatter and Webpack loader.
10
-
11
- ## Installation 💻
12
-
13
- > In April 2023, we released version 3 of this package, which includes breaking changes. Please refer to the [upgrade guide](./V2-TO-V3-UPGRADE-GUIDE.md) 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 library written entirely in TypeScript that exactly reproduces the [Properties Java implementation](/assets/java-implementation.md).
24
- - Works for both Node.js applications and browsers that support at least [ES5](https://www.w3schools.com/js/js_es5.asp).
25
- - Flexible APIs:
26
- - `getProperties` converts the content of `.properties` files to a key-value pair object.
27
- - A `Properties` class provides insights into parsing data.
28
- - A `PropertiesEditor` class enables the addition, edition, and removal of entries.
29
- - `escapeKey` and `escapeValue` allow the conversion of any content to a `.properties` compatible format.
30
- - The library also includes a Webpack loader to import `.properties` files directly into your application.
31
- - [Tiny](https://bundlejs.com/?q=properties-file%40latest&treeshake=%5B*%5D) with 0 dependencies.
32
- - 100% test coverage based on the output from a Java implementation.
33
- - Active maintenance (many popular `.properties` packages have been inactive for years).
34
-
35
- ## Usage 🎬
36
-
37
- We have put a lot of effort into incorporating [TSDoc](https://tsdoc.org/) into all our APIs. If you are unsure about how to use certain APIs provided in our examples, please check directly in your IDE.
38
-
39
- ### `getProperties` (converting `.properties` to an object)
40
-
41
- The most common use case for `.properties` files is for Node.js applications that need to read the file's content into a simple key-value pair object. Here is how this can be done with a single API call:
42
-
43
- ```ts
44
- import { readFileSync } from 'node:fs'
45
- import { getProperties } from 'properties-file'
46
-
47
- console.log(getProperties(readFileSync('hello-world.properties')))
48
- ```
49
-
50
- Output:
51
-
52
- ```js
53
- { hello: 'hello', world: 'world' }
54
- ```
55
-
56
- ### `Properties` (using parsing metadata)
57
-
58
- The `Properties` object is what makes `getProperties` work under the hood, but when using it directly, you can access granular parsing metadata. Here is an example of how the object can be used to find key collisions:
59
-
60
- ```ts
61
- import { Properties } from 'properties-file'
62
-
63
- const properties = new Properties(
64
- 'hello = hello1\nworld = world1\nworld = world2\nhello = hello2\nworld = world3'
65
- )
66
- console.log(properties.format())
67
-
68
- /**
69
- * Outputs:
70
- *
71
- * hello = hello1
72
- * world = world1
73
- * world = world2
74
- * hello = hello2
75
- * world = world3
76
- */
77
-
78
- properties.collection.forEach((property) => {
79
- console.log(`${property.key} = ${property.value}`)
80
- })
81
-
82
- /**
83
- * Outputs:
84
- *
85
- * hello = hello2
86
- * world = world3
87
- */
88
-
89
- const keyCollisions = properties.getKeyCollisions()
90
-
91
- keyCollisions.forEach((keyCollision) => {
92
- console.warn(
93
- `Found a key collision for key '${
94
- keyCollision.key
95
- }' on lines ${keyCollision.startingLineNumbers.join(
96
- ', '
97
- )} (will use the value at line ${keyCollision.getApplicableLineNumber()}).`
98
- )
99
- })
100
-
101
- /**
102
- * Outputs:
103
- *
104
- * Found a key collision for key 'hello' on lines 1, 4 (will use the value at line 4).
105
- * Found a key collision for key 'world' on lines 2, 3, 5 (will use the value at line 5).
106
- */
107
- ```
108
-
109
- For purposes where you require more parsing metadata, such as building a syntax highlighter, it is recommended that you access the `Property` objects included in the `Properties.collection`. These objects provide comprehensive information about each key-value pair.
110
-
111
- ### `PropertiesEditor` (editing `.properties` content)
112
-
113
- In certain scenarios, it may be necessary to modify the content of the `.properties` key-value pair objects. This can be achieved easily using the `Properties` object, with the assistance of the `escapeKey` and `escapeValue` APIs, as demonstrated below:
114
-
115
- ```ts
116
- import { Properties } from 'properties-file'
117
- import { escapeKey, escapeValue } from 'properties-file/escape'
118
-
119
- const properties = new Properties('hello = hello\n# This is a comment\nworld = world')
120
- const newProperties: string[] = []
121
-
122
- properties.collection.forEach((property) => {
123
- const key = property.key === 'world' ? 'new world' : property.key
124
- const value = property.value === 'world' ? 'new world' : property.value
125
- newProperties.push(`${escapeKey(key)} = ${escapeValue(value)}`)
126
- })
127
-
128
- console.log(newProperties.join('\n'))
129
-
130
- /**
131
- * Outputs:
132
- *
133
- * hello = hello
134
- * new\ world = new world
135
- */
136
- ```
137
-
138
- The limitation of this approach is that its output contains only valid keys, without any comments or whitespace. However, if you require a more advanced editor that preserves these original elements, then the `PropertiesEditor` object is exactly what you need.
139
-
140
- ```ts
141
- import { PropertiesEditor } from 'properties-file/editor'
142
-
143
- const properties = new PropertiesEditor('hello = hello\n# This is a comment\nworld = world')
144
- console.log(properties.format())
145
-
146
- /**
147
- * Outputs:
148
- *
149
- * hello = hello
150
- * # This is a comment
151
- * world = world
152
- */
153
-
154
- properties.insertComment('This is a multiline\ncomment before `newKey3`')
155
- properties.insert('newKey3', 'This is my third key')
156
-
157
- properties.insert('newKey1', 'This is my first new key', {
158
- referenceKey: 'newKey3',
159
- position: 'before',
160
- comment: 'Below are the new keys being edited',
161
- commentDelimiter: '!',
162
- })
163
-
164
- properties.insert('newKey2', 'こんにちは', {
165
- referenceKey: 'newKey1',
166
- position: 'after',
167
- escapeUnicode: true,
168
- })
169
-
170
- properties.delete('hello')
171
- properties.update('world', {
172
- newValue: 'new world',
173
- })
174
- console.log(properties.format())
175
-
176
- /**
177
- * Outputs:
178
- *
179
- * # This is a comment
180
- * world = new world
181
- * ! Below are the new keys being edited
182
- * newKey1 = This is my first new key
183
- * newKey2 = \u3053\u3093\u306b\u3061\u306f
184
- * # This is a multiline
185
- * # comment before `newKey3`
186
- * newKey3 = This is my third key
187
- */
188
- ```
189
-
190
- For convenience, we also added an `upsert` method that allows updating a key if it exists or adding it at the end, when it doesn't. Make sure to check in your IDE for all available methods and options in our TSDoc.
191
-
192
- ### Webpack File Loader
193
-
194
- 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:
195
-
196
- ```js
197
- // webpack.config.js
198
- module.exports = {
199
- module: {
200
- rules: [
201
- {
202
- test: /\.properties$/i,
203
- use: [
204
- {
205
- loader: 'properties-file/webpack-loader',
206
- },
207
- ],
208
- },
209
- ],
210
- },
211
- }
212
- ```
213
-
214
- 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:
215
-
216
- ```ts
217
- declare module '*.properties' {
218
- /** A key/value object representing the content of a `.properties` file. */
219
- const properties: {
220
- /** The value of a `.properties` file key. */
221
- [key: string]: string
222
- }
223
- export { properties }
224
- }
225
- ```
226
-
227
- By adding these configurations you should now be able to import directly `.properties` files just like this:
228
-
229
- ```ts
230
- import { properties as helloWorld } from './hello-world.properties'
231
-
232
- console.dir(helloWorld)
233
- ```
234
-
235
- Output:
236
-
237
- ```json
238
- { "hello": "world" }
239
- ```
240
-
241
- ## Why another `.properties` file package?
242
-
243
- There are probably over 20 similar packages available, but:
244
-
245
- - Many of the most popular packages have had no activity for over 5 years.
246
- - Most packages will not replicate the current Java implementation.
247
- - No package offers the same capabilities as this one.
248
-
249
- Unfortunately, the `.properties` file specification is not well-documented. One reason for this is that it was originally used in Java to store configurations. Today, most applications handle this using JSON, YAML, or other modern formats because these formats are more flexible.
250
-
251
- ### So why `.properties` files?
252
-
253
- While many options exist today to handle configurations, `.properties` files remain one of the best options 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):
254
-
255
- | File format | Key/value based | Supports inline comments | Built for localization | Good linguistic tools support |
256
- | ------------- | ---------------- | ------------------------ | ---------------------- | ----------------------------- |
257
- | `.properties` | Yes | Yes | Yes (Resource Bundles) | Yes |
258
- | `JSON` | No (can do more) | No (requires JSON5) | No | Depends on the schema |
259
- | `YAML` | No (can do more) | Yes | No | Depends on the schema |
260
-
261
- Having good JavaScript/TypeScript support for `.properties` files offers more internationalization (i18n) options.
262
-
263
- ### How does this package work?
264
-
265
- Basically, our goal was to offer parity with the Java implementation, which is the closest thing to a specification for `.properties` files. Here is the logic behind this package in a nutshell:
266
-
267
- 1. The content is split by lines, creating an array of strings where each line is an element.
268
- 2. All lines are parsed to create a collection of `Property` objects that:
269
- 1. Identify key-value pair lines from the other lines (e.g., comments, blank lines, etc.).
270
- 2. Merge back multiline key-value pairs on single lines by removing trailing backslashes.
271
- 3. Unescape the keys and values.
272
-
273
- Just like Java, if a Unicode-escaped character (`\u`) is malformed, an error will be thrown. However, we do not recommend using Unicode-escaped characters, but rather using UTF-8 encoding that supports more characters.
274
-
275
- ## Additional references
276
-
277
- - Java [Test Sandbox](https://codehs.com/sandbox/id/java-main-FObePj)
278
- - Java's `Properties` class [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/Properties.html)
279
- - Java's `PropertyResourceBundle` [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/PropertyResourceBundle.html)
280
- - Java's Internationalization [Guide](https://docs.oracle.com/en/java/javase/18/intl/internationalization-overview.html)
281
- - Wikipedia's .properties [page](https://en.wikipedia.org/wiki/.properties)
282
-
283
- ### Special mention
284
-
285
- 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.
1
+ # properties-file
2
+
3
+ [![License](https://img.shields.io/npm/l/make-coverage-badge.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
4
+ [![Download Stats](https://img.shields.io/npm/dw/properties-file.svg?color=brightgreen)](https://www.npmjs.com/package/properties-file)
5
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)
6
+ ![Package Size](https://img.shields.io/badge/min%2Bgzip-1.1%20kB-brightgreen)
7
+ ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)
8
+
9
+ `.properties` file parser, editor, formatter and bundler integrations.
10
+
11
+ ## Installation
12
+
13
+ > Doing a major version update? Check our [migration guides](./docs/migration/README.md).
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 library written entirely in TypeScript that exactly reproduces the [Properties Java implementation](/assets/java-implementation.md).
24
+ - Works for both Node.js applications and browsers that support at least [ES5](https://www.w3schools.com/js/js_es5.asp).
25
+ - Flexible, tree-shakable APIs — import only what you need, and your bundler will exclude the rest:
26
+ - `getProperties` converts `.properties` content to a key-value pair object.
27
+ - `Properties` provides lossless parsing with a full data model — every element (properties, comments, blank lines, whitespace, duplicate keys) is preserved and can be round-tripped exactly or normalized via `format()` options.
28
+ - `PropertiesEditor` enables insertion, edition, and removal of entries while preserving formatting.
29
+ - `escapeKey` and `escapeValue` convert any content to `.properties` compatible format.
30
+ - Bundler integrations for Webpack, Rollup/Vite, esbuild, and Bun to import `.properties` files directly. See [BUNDLER.md](./docs/BUNDLER.md).
31
+ - **Tiny with 0 dependencies** — `getProperties` is only 1.1 kB min+gzip.
32
+ - **Runs everywhere** compiled to ES5, works in any browser and on Node.js all the way back to v0.10 (2013). [Verified via Docker](./tests/node-compat/).
33
+ - **100% test coverage** based on the output from a Java implementation.
34
+ - Active maintenance (many popular `.properties` packages have been inactive for years). See our [detailed comparison](./docs/COMPARISON.md) with other packages.
35
+
36
+ ## Usage
37
+
38
+ We have put a lot of effort into incorporating [TSDoc](https://tsdoc.org/) into all our APIs. If you are unsure about how to use certain APIs provided in our examples, please check directly in your IDE.
39
+
40
+ ### `getProperties` (converting `.properties` to an object)
41
+
42
+ The most common use case for `.properties` files is for Node.js applications that need to read the file's content into a simple key-value pair object. Here is how this can be done with a single API call:
43
+
44
+ ```ts
45
+ import { readFileSync } from 'node:fs'
46
+ import { getProperties } from 'properties-file'
47
+
48
+ console.log(getProperties(readFileSync('hello-world.properties')))
49
+ ```
50
+
51
+ Output:
52
+
53
+ ```js
54
+ { hello: 'hello', world: 'world' }
55
+ ```
56
+
57
+ ### `Properties` (lossless parsing with full data model)
58
+
59
+ The `Properties` class parses a `.properties` file into a lossless data model where every element — properties, comments, blank lines — is preserved in order. This is useful when you need to inspect, analyze, or transform `.properties` files while retaining their exact structure.
60
+
61
+ ```ts
62
+ import { readFileSync } from 'node:fs'
63
+ import { PropertiesNodeType, Properties } from 'properties-file/parser'
64
+
65
+ const properties = new Properties(readFileSync('example.properties'))
66
+
67
+ // Access all nodes in file order (properties, comments, blank lines).
68
+ for (const node of properties.nodes) {
69
+ switch (node.type) {
70
+ case PropertiesNodeType.PROPERTY:
71
+ console.log(`${node.key} = ${node.value}`)
72
+ break
73
+ case PropertiesNodeType.COMMENT:
74
+ console.log(`Comment: ${node.delimiter}${node.body}`)
75
+ break
76
+ case PropertiesNodeType.BLANK:
77
+ console.log('(blank line)')
78
+ break
79
+ }
80
+ }
81
+
82
+ // Get a simple key-value object (last-wins for duplicate keys).
83
+ console.log(properties.toObject())
84
+
85
+ // Lossless round-trip: format() reproduces the exact original content.
86
+ console.log(properties.format() === readFileSync('example.properties', 'utf8')) // true
87
+ ```
88
+
89
+ #### Finding key collisions
90
+
91
+ ```ts
92
+ import { Properties } from 'properties-file/parser'
93
+
94
+ const properties = new Properties(
95
+ 'hello = hello1\nworld = world1\nworld = world2\nhello = hello2\nworld = world3'
96
+ )
97
+
98
+ const collisions = properties.getKeyCollisions()
99
+ collisions.forEach((collision) => {
100
+ const lines = collision.nodes.map((node) => node.startingLineNumber)
101
+ console.log(`Key '${collision.key}' appears on lines ${lines.join(', ')}`)
102
+ })
103
+
104
+ /**
105
+ * Outputs:
106
+ *
107
+ * Key 'hello' appears on lines 1, 4
108
+ * Key 'world' appears on lines 2, 3, 5
109
+ */
110
+ ```
111
+
112
+ #### Normalizing output
113
+
114
+ Passing options to `format()` produces a normalized version of the file with granular control over formatting:
115
+
116
+ ```ts
117
+ import { Properties } from 'properties-file/parser'
118
+
119
+ const properties = new Properties('# comment\n\n key : value\n key : updated')
120
+
121
+ console.log(
122
+ properties.format({
123
+ removeComments: true, // Strip all comments
124
+ removeBlankLines: true, // Strip all blank lines
125
+ removeLeadingWhitespace: true, // Strip indentation
126
+ deduplicateKeys: true, // Keep only last occurrence
127
+ separatorChar: '=', // Standardize separator
128
+ separatorLeading: ' ', // Space before =
129
+ separatorTrailing: ' ', // Space after =
130
+ })
131
+ )
132
+
133
+ /**
134
+ * Outputs:
135
+ *
136
+ * key = updated
137
+ */
138
+ ```
139
+
140
+ ### `PropertiesEditor` (editing `.properties` content)
141
+
142
+ The `PropertiesEditor` extends `Properties` with methods to insert, update, delete, and upsert entries while preserving formatting.
143
+
144
+ ```ts
145
+ import { PropertiesEditor } from 'properties-file/editor'
146
+
147
+ const properties = new PropertiesEditor('hello = hello\n# This is a comment\nworld = world')
148
+
149
+ properties.insertComment('This is a multiline\ncomment before `newKey3`')
150
+ properties.insert('newKey3', 'This is my third key')
151
+
152
+ properties.insert('newKey1', 'This is my first new key', {
153
+ referenceKey: 'newKey3',
154
+ position: 'before',
155
+ comment: 'Below are the new keys being edited',
156
+ commentDelimiter: '!',
157
+ })
158
+
159
+ properties.insert('newKey2', 'hello', {
160
+ referenceKey: 'newKey1',
161
+ position: 'after',
162
+ escapeUnicode: true,
163
+ })
164
+
165
+ properties.delete('hello')
166
+ properties.update('world', {
167
+ newValue: 'new world',
168
+ })
169
+ console.log(properties.format())
170
+
171
+ /**
172
+ * Outputs:
173
+ *
174
+ * # This is a comment
175
+ * world = new world
176
+ * ! Below are the new keys being edited
177
+ * newKey1 = This is my first new key
178
+ * newKey2 = hello
179
+ * # This is a multiline
180
+ * # comment before `newKey3`
181
+ * newKey3 = This is my third key
182
+ */
183
+ ```
184
+
185
+ The editor also provides `upsert` (update or insert) and `deleteAll` (remove all occurrences of a duplicate key). Check your IDE for all available methods and options via TSDoc.
186
+
187
+ ### Bundler Integrations
188
+
189
+ If you would like to import `.properties` directly using `import`, this package provides integrations for all major bundlers: **Webpack/Rspack**, **Rollup/Vite/Rolldown**, **esbuild**, and **Bun**.
190
+
191
+ See [BUNDLER.md](./docs/BUNDLER.md) for setup instructions and examples.
192
+
193
+ By adding these configurations you should now be able to import directly `.properties` files just like this:
194
+
195
+ ```ts
196
+ import { properties as helloWorld } from './hello-world.properties'
197
+
198
+ console.dir(helloWorld)
199
+ ```
200
+
201
+ Output:
202
+
203
+ ```json
204
+ { "hello": "world" }
205
+ ```
206
+
207
+ ## Why another `.properties` file package?
208
+
209
+ There are over 20 similar packages available, but most are abandoned, incomplete, or not compliant with the Java specification. See our [detailed comparison](./docs/COMPARISON.md) for benchmarks, compliance tests, and a feature matrix against the top 5 packages. The short version:
210
+
211
+ - **100% Java spec compliance** — the only package (alongside `properties-parser`) to pass all test cases.
212
+ - **3–7x faster** than alternatives on a 10,000-entry file.
213
+ - **Lossless data model** — no other package preserves comments, blank lines, whitespace, and duplicate keys for round-trip editing.
214
+
215
+ Unfortunately, the `.properties` file specification is not well-documented. One reason for this is that it was originally used in Java to store configurations. Today, most applications handle this using JSON, YAML, or other modern formats because these formats are more flexible.
216
+
217
+ ### So why `.properties` files?
218
+
219
+ While many options exist today to handle configurations, `.properties` files remain one of the best options 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):
220
+
221
+ | File format | Key/value based | Supports inline comments | Built for localization | Good linguistic tools support |
222
+ | ------------- | ---------------- | ------------------------ | ---------------------- | ----------------------------- |
223
+ | `.properties` | Yes | Yes | Yes (Resource Bundles) | Yes |
224
+ | `JSON` | No (can do more) | No (requires JSON5) | No | Depends on the schema |
225
+ | `YAML` | No (can do more) | Yes | No | Depends on the schema |
226
+
227
+ Having good JavaScript/TypeScript support for `.properties` files offers more internationalization (i18n) options.
228
+
229
+ ### How does this package work?
230
+
231
+ Our goal is to offer parity with the Java implementation, which is the closest thing to a specification for `.properties` files. The package provides two parsing paths:
232
+
233
+ 1. **`getProperties`** — a fast, functional parser optimized for the common case of converting `.properties` content to a key-value object. Uses `charCodeAt`-based scanning with zero-copy optimizations.
234
+
235
+ 2. **`Properties`** — a lossless parser that produces an ordered array of typed nodes (`PropertyNode`, `CommentNode`, `BlankLineNode`). Every element in the file is preserved, enabling exact round-trip reconstruction via `format()` and flexible normalization by passing options to `format()`.
236
+
237
+ Both parsers are fully compliant with the Java `Properties` specification and produce identical key-value output. Just like Java, if a Unicode-escaped character (`\u`) is malformed, an error will be thrown.
238
+
239
+ ## Contributing
240
+
241
+ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for project principles, architecture, code style, and development commands.
242
+
243
+ ## Additional references
244
+
245
+ - Java [Test Sandbox](https://codehs.com/sandbox/id/java-main-FObePj)
246
+ - Java's `Properties` class [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/Properties.html)
247
+ - Java's `PropertyResourceBundle` [documentation](https://docs.oracle.com/javase/9/docs/api/java/util/PropertyResourceBundle.html)
248
+ - Java's Internationalization [Guide](https://docs.oracle.com/en/java/javase/18/intl/internationalization-overview.html)
249
+ - Wikipedia's .properties [page](https://en.wikipedia.org/wiki/.properties)
250
+
251
+ ### Special mention
252
+
253
+ 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,10 @@
1
+ import type { BunPlugin } from 'bun';
2
+ /**
3
+ * Bun plugin for `.properties` files. Works with both `Bun.plugin` (runtime) and `Bun.build`
4
+ * (build-time).
5
+ */
6
+ declare const bunPlugin: BunPlugin;
7
+ export default bunPlugin;
8
+
9
+ // Enables type recognition for direct `.properties` file imports.
10
+ import '../properties-file.d.ts'
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return _default}});var _nodefs=require("node:fs"),_index=require("../index.js"),bunPlugin={name:"properties-file",setup:function(e){e.onLoad({filter:/\.properties$/},function(e){var r=e.path;return{exports:{properties:(0,_index.getProperties)((0,_nodefs.readFileSync)(r,"utf8"))},loader:"object"}})}},_default=bunPlugin;
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from 'esbuild';
2
+ /**
3
+ * esbuild plugin for `.properties` files.
4
+ *
5
+ * @returns An esbuild plugin that transforms `.properties` imports into JavaScript modules.
6
+ */
7
+ declare const esbuildPlugin: () => Plugin;
8
+ export default esbuildPlugin;
9
+
10
+ // Enables type recognition for direct `.properties` file imports.
11
+ import '../properties-file.d.ts'
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return _default}});var _nodefs=require("node:fs"),_index=require("../index.js"),esbuildPlugin=function(){return{name:"properties-file",setup:function(e){e.onLoad({filter:/\.properties$/},function(e){var t=e.path;return{contents:"export const properties = ".concat(JSON.stringify((0,_index.getProperties)((0,_nodefs.readFileSync)(t,"utf8"))),";"),loader:"js"}})}}},_default=esbuildPlugin;
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from 'rollup';
2
+ /**
3
+ * Rollup plugin for `.properties` files. Also compatible with Vite and Rolldown.
4
+ *
5
+ * @returns A Rollup plugin that transforms `.properties` imports into JavaScript modules.
6
+ */
7
+ declare const rollupPlugin: () => Plugin;
8
+ export default rollupPlugin;
9
+
10
+ // Enables type recognition for direct `.properties` file imports.
11
+ import '../properties-file.d.ts'
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return _default}});var _index=require("../index.js"),PROPERTIES_EXTENSION=".properties",rollupPlugin=function(){return{name:"properties-file",transform:function(e,r){return-1===r.indexOf(PROPERTIES_EXTENSION,r.length-PROPERTIES_EXTENSION.length)?null:{code:"export const properties = ".concat(JSON.stringify((0,_index.getProperties)(e)),";"),map:null}}}},_default=rollupPlugin;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Webpack file loader for `.properties` files. Also compatible with Rspack.
3
+ *
4
+ * @param content - The content of a `.properties` file.
5
+ *
6
+ * @returns A CommonJS module string exporting the parsed key-value pairs.
7
+ */
8
+ declare const webpackLoader: (content: string) => string;
9
+ export default webpackLoader;
10
+
11
+ // Enables type recognition for direct `.properties` file imports.
12
+ import '../properties-file.d.ts'
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"default",{enumerable:!0,get:function(){return _default}});var _index=require("../index.js"),webpackLoader=function(e){return"exports.properties = ".concat(JSON.stringify((0,_index.getProperties)(e)),";")},_default=webpackLoader;